<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8724348233587699474</id><updated>2026-04-24T20:33:36.161-04:00</updated><category term="flickr"/><category term="Linux"/><category term="youtube"/><category term="Blender 3D"/><category term="Harmony App"/><category term="Python"/><category term="custom"/><category term="video"/><category term="TUI"/><category term="Terminal"/><category term="Textual"/><category term="UBUNTU"/><category term="android"/><category term="appliance"/><category term="celebration"/><category term="customize"/><category term="devices"/><category term="google"/><category term="india"/><category term="nexus 7"/><category term="opensource"/><category term="photos"/><category term="webpage"/><category term="150 years"/><category term="16"/><category term="2014"/><category term="2015"/><category term="2015 ride for heart"/><category term="API Migration"/><category term="ASCII Art"/><category term="Address Manager"/><category term="Animation"/><category term="Blogger"/><category term="BlueCat"/><category term="Cisco"/><category term="Confederation"/><category term="DDI"/><category term="DHCP"/><category term="DNS"/><category term="Deco Sketch"/><category term="Developer Experience"/><category term="Floral"/><category term="Game Development"/><category term="I am"/><category term="IPAM"/><category term="JeSuisCharlie"/><category term="LLM"/><category term="MS Word"/><category term="Moon Traveler"/><category term="Niagara falls"/><category term="Open Source"/><category term="REST API"/><category term="Rich"/><category term="Snap Seed"/><category term="Sprites"/><category term="UX Design"/><category term="Windows 7"/><category term="adam and eve"/><category term="aerial"/><category term="ai"/><category term="architecture"/><category term="automation"/><category term="bank of canada"/><category term="becel"/><category term="behance"/><category term="bible"/><category term="birthday"/><category term="bryan lunduke"/><category term="builders"/><category term="canada"/><category term="canolgon"/><category term="chat"/><category term="cheap"/><category term="coffee table"/><category term="color"/><category term="command"/><category term="cool"/><category term="design"/><category term="development"/><category term="editing"/><category term="falls"/><category term="find"/><category term="free"/><category term="funny"/><category term="galaxy nexus"/><category term="google sketchup"/><category term="heartandstroke"/><category term="holi"/><category term="infographic"/><category term="learning"/><category term="linkedin"/><category term="mars"/><category term="mobile creation"/><category term="nature"/><category term="network automation"/><category term="networktocode"/><category term="niagara"/><category term="paintnite"/><category term="password"/><category term="pc"/><category term="photography"/><category term="photoshop"/><category term="protection"/><category term="pulse"/><category term="reasons"/><category term="responsive"/><category term="ride for heart"/><category term="security"/><category term="smartphone"/><category term="song"/><category term="space"/><category term="stage"/><category term="sting"/><category term="table"/><category term="talk"/><category term="ted"/><category term="the office pub"/><category term="tips"/><category term="training"/><category term="tricks"/><category term="tutorial"/><category term="typescript"/><category term="under $50"/><category term="upcoming"/><category term="wallpaper"/><category term="water"/><category term="water paint"/><category term="windows"/><category term="word."/><title type='text'>Technology Mystery</title><subtitle type='html'>So you are curious about technology where is computerized or just old fashioned mechanical, used in everyday life or professional life. Here is my way through some that, you might be interested in.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>49</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-8071705985059452232</id><published>2026-04-24T13:53:53.705-04:00</published><updated>2026-04-24T13:53:53.706-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Address Manager"/><category scheme="http://www.blogger.com/atom/ns#" term="API Migration"/><category scheme="http://www.blogger.com/atom/ns#" term="automation"/><category scheme="http://www.blogger.com/atom/ns#" term="BlueCat"/><category scheme="http://www.blogger.com/atom/ns#" term="DDI"/><category scheme="http://www.blogger.com/atom/ns#" term="DHCP"/><category scheme="http://www.blogger.com/atom/ns#" term="DNS"/><category scheme="http://www.blogger.com/atom/ns#" term="IPAM"/><category scheme="http://www.blogger.com/atom/ns#" term="network automation"/><category scheme="http://www.blogger.com/atom/ns#" term="networktocode"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="REST API"/><category scheme="http://www.blogger.com/atom/ns#" term="typescript"/><title type='text'>Migrating from BlueCat BAM v1 to v2 REST API: Authentication, Code Examples, and What Changed</title><content type='html'>&lt;!--
Blog post for: https://techmystery.blogspot.com/
Title: Migrating from BlueCat BAM v1 to v2 REST API: Authentication, Code Examples, and What Changed
Author: elephantatech
Date: 2026-04-24
--&gt;

&lt;h2&gt;Why This Post Exists&lt;/h2&gt;

&lt;p&gt;If you&#39;ve worked with BlueCat Address Manager&#39;s REST API, you&#39;ve probably used the v1 endpoints under &lt;code&gt;/Services/REST/v1/&lt;/code&gt;. Those examples are all over the internet -- including BlueCat&#39;s own &lt;a href=&quot;https://github.com/bluecatlabs/making-apis-work-for-you&quot; target=&quot;_blank&quot;&gt;making-apis-work-for-you&lt;/a&gt; repo on GitHub, which was the go-to reference for years.&lt;/p&gt;

&lt;p&gt;The problem: &lt;strong&gt;v1 is deprecated&lt;/strong&gt;. BlueCat introduced the RESTful v2 API in BAM 9.5.0, and the v1 API is now officially called the &quot;Legacy v1 API.&quot; If you&#39;re writing new integrations or maintaining existing ones, you need to migrate.&lt;/p&gt;

&lt;p&gt;I went looking for an updated version of those examples and found that &lt;strong&gt;nobody had done it&lt;/strong&gt;. The original &lt;code&gt;making-apis-work-for-you&lt;/code&gt; repo hasn&#39;t been meaningfully updated since 2018. The example scripts are a mix of Python 2 and Python 3, and the REST examples all target the v1 API. BlueCat shipped the v2 API years ago, published migration guides, even released an official Python SDK (&lt;a href=&quot;https://pypi.org/project/bluecat-libraries/&quot; target=&quot;_blank&quot;&gt;bluecat-libraries&lt;/a&gt;) -- but the reference examples that most people find first on Google and GitHub were never brought forward. There are a handful of community wrappers on GitHub (see &lt;a href=&quot;https://github.com/ForrestT/pyBluecat&quot; target=&quot;_blank&quot;&gt;pyBluecat&lt;/a&gt;, &lt;a href=&quot;https://github.com/marcowartmann/py-bluecat-bam-sdk&quot; target=&quot;_blank&quot;&gt;py-bluecat-bam-sdk&lt;/a&gt;), but none of them provide the kind of simple, runnable, &quot;Episode 1 through 7&quot; walkthrough that the original repo did.&lt;/p&gt;

&lt;p&gt;So I built one. I rewrote every example from the original BlueCat repo using the v2 API in both Python and TypeScript, added an office network template provisioner that didn&#39;t exist before, and open-sourced it all under Apache 2.0: &lt;a href=&quot;https://github.com/elephantatech/bluecat-bam-v2-examples&quot; target=&quot;_blank&quot;&gt;elephantatech/bluecat-bam-v2-examples&lt;/a&gt;. This post walks through the biggest changes, with code you can copy.&lt;/p&gt;

&lt;h2&gt;Official docs&lt;/h2&gt;

&lt;p&gt;Here are the docs you&#39;ll want bookmarked:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Introduction/25.1.0&quot; target=&quot;_blank&quot;&gt;BAM RESTful v2 API Guide (25.1.0)&lt;/a&gt; -- the primary reference&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.bluecatnetworks.com/r/en-US/Address-Manager-Legacy-v1-API-Guide/9.5.0&quot; target=&quot;_blank&quot;&gt;BAM Legacy v1 API Guide (9.5.0)&lt;/a&gt; -- if you&#39;re migrating off v1&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/v1-REST-API-to-RESTful-v2-API-migration-guide/9.5.0&quot; target=&quot;_blank&quot;&gt;v1 to v2 Migration Guide&lt;/a&gt; -- BlueCat&#39;s official migration reference&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.bluecatnetworks.com/r/BlueCat-Python-Library-Guide/BlueCat-Library-Address-Manager-REST-v2-API-client-reference/23.1&quot; target=&quot;_blank&quot;&gt;BlueCat Python Library (bluecat-libraries)&lt;/a&gt; -- official Python SDK&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypi.org/project/bluecat-libraries/&quot; target=&quot;_blank&quot;&gt;bluecat-libraries on PyPI&lt;/a&gt; -- official package, published and maintained by BlueCat Networks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every BAM instance also ships with interactive Swagger docs at &lt;code&gt;https://&amp;lt;your-bam&amp;gt;/api/docs&lt;/code&gt; and the OpenAPI 3.0 spec at &lt;code&gt;https://&amp;lt;your-bam&amp;gt;/api/openapi.json&lt;/code&gt;.&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;Before you write any code: BAM-side setup&lt;/h2&gt;

&lt;p&gt;The API is always on -- there&#39;s no switch to enable it. But your user account has to be configured correctly or every call will fail with a 401. This trips people up because a user who works fine in the web UI might not have API access at all.&lt;/p&gt;

&lt;h3&gt;Create a dedicated API user&lt;/h3&gt;

&lt;p&gt;In the BAM web UI, go to &lt;strong&gt;Administration &gt; Users and Groups&lt;/strong&gt; and create a new user. The fields that matter (full docs: &lt;a href=&quot;https://docs.bluecatnetworks.com/r/Address-Manager-Legacy-v1-API-Guide/Creating-an-API-user-in-Address-Manager/9.5.0&quot; target=&quot;_blank&quot;&gt;Creating an API user in Address Manager&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Username&lt;/strong&gt; -- something like &lt;code&gt;svc-provisioner&lt;/code&gt; or &lt;code&gt;api-automation&lt;/code&gt;. Don&#39;t reuse a personal account for scripts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Access Type&lt;/strong&gt; -- this is the one that bites you. There are three options:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;GUI&lt;/strong&gt; -- web UI only. Cannot use the API at all.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;API&lt;/strong&gt; -- API only. Cannot log in to the web UI.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;GUI and API&lt;/strong&gt; -- both.&lt;/li&gt;
    &lt;/ul&gt;
    For automation, use &lt;strong&gt;API&lt;/strong&gt;. If the user is set to &quot;GUI&quot; only, your login calls will fail, make sure API is also selected for api access. Additionally you also need read and/or write access the object on Bluecat Address Manager.
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;User Type&lt;/strong&gt; -- &lt;strong&gt;Administrator&lt;/strong&gt; has access to everything (DNS, IPAM, DHCP, system settings). &lt;strong&gt;Non-Administrator&lt;/strong&gt; is limited to DNS and IPAM only and needs explicit access rights on every object it should touch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Set access rights&lt;/h3&gt;

&lt;p&gt;If you&#39;re using a Non-Administrator user, you need to grant access rights on the objects the user will work with. Access rights are hierarchical -- they&#39;re set per object (Configuration, Block, View, Zone, Network) and inherit down the tree. The levels are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;View&lt;/strong&gt; -- read-only&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Add&lt;/strong&gt; -- can create child objects&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Change&lt;/strong&gt; -- can modify objects&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Full Access&lt;/strong&gt; -- create, modify, delete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a provisioner that creates networks, zones, and DHCP scopes, you need at least &lt;strong&gt;Full Access&lt;/strong&gt; on the target Configuration. Or you can be more granular and set Add + Change on specific Blocks and Views. An Administrator user skips all of this -- it has implicit full access everywhere.&lt;/p&gt;

&lt;h3&gt;Other things to check&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;HTTPS&lt;/strong&gt; -- BAM ships with a self-signed certificate. Your API client will reject it unless you either import the cert into your trust store or disable verification (&lt;code&gt;verify=False&lt;/code&gt; in Python, &lt;code&gt;rejectUnauthorized: false&lt;/code&gt; in Bun). For production, get a proper CA-signed cert on the BAM -- configure it under &lt;strong&gt;Administration &gt; HTTPS Configuration&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Session timeout&lt;/strong&gt; -- the API token lifetime is tied to the BAM session inactivity timeout. The v2 session response includes &lt;code&gt;apiTokenExpirationDateTime&lt;/code&gt; (usually 24 hours). If your script runs longer than the timeout, you&#39;ll need to re-authenticate.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Firewall&lt;/strong&gt; -- BAM has a built-in firewall under &lt;strong&gt;Administration &gt; Firewall&lt;/strong&gt;. There&#39;s no API-specific IP allowlist, but you can restrict which IPs can reach BAM on port 443. If your automation server is getting connection refused, check here.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No rate limiting&lt;/strong&gt; -- BAM doesn&#39;t throttle API requests. But it&#39;s an appliance with finite resources, so don&#39;t hammer it with hundreds of concurrent sessions. Reuse your session and call logout when you&#39;re done.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr/&gt;

&lt;h2&gt;What changed&lt;/h2&gt;

&lt;table border=&quot;1&quot; cellpadding=&quot;8&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse; width:100%;&quot;&gt;
  &lt;thead&gt;
    &lt;tr style=&quot;background:#f0f0f0;&quot;&gt;
      &lt;th&gt;Area&lt;/th&gt;
      &lt;th&gt;v1 (Deprecated)&lt;/th&gt;
      &lt;th&gt;v2 (Current)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Base URL&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/Services/REST/v1/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;/api/v2/&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Auth&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;GET with credentials in URL&lt;/td&gt;
      &lt;td&gt;POST with JSON body&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Token format&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Parse from string: &lt;code&gt;&quot;Session Token-&gt; BAMAuthToken: abc123&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;JSON field: &lt;code&gt;{&quot;apiToken&quot;: &quot;abc123&quot;}&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;API style&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;RPC: &lt;code&gt;POST /v1/addHostRecord?viewId=123&amp;amp;absoluteName=...&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;REST: &lt;code&gt;POST /api/v2/zones/456/resourceRecords&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Response format&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Pipe-delimited properties: &lt;code&gt;&quot;name=x|connected=true|&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Clean JSON objects&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Hierarchy traversal&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;getEntityByName&lt;/code&gt; calls chained manually&lt;/td&gt;
      &lt;td&gt;Resource paths: &lt;code&gt;/configurations/{id}/views/{id}/zones&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Static docs only&lt;/td&gt;
      &lt;td&gt;OpenAPI 3.0 spec + Swagger UI on every BAM instance&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr/&gt;

&lt;h2&gt;BlueCat&#39;s Official Python SDK: bluecat-libraries&lt;/h2&gt;

&lt;p&gt;BlueCat &lt;em&gt;does&lt;/em&gt; have an official Python SDK. The &lt;a href=&quot;https://pypi.org/project/bluecat-libraries/&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;bluecat-libraries&lt;/code&gt;&lt;/a&gt; package on PyPI is &lt;strong&gt;published and maintained directly by BlueCat Networks&lt;/strong&gt;. I verified this -- the PyPI metadata lists &quot;BlueCat&quot; as both author and maintainer, the homepage points to &lt;code&gt;docs.bluecatnetworks.com&lt;/code&gt;, and the copyright is &quot;BlueCat Networks (USA) Inc. and its affiliates and licensors.&quot; It covers the v2 API, the legacy v1 API, Failover, DNS Edge, and Micetro. Latest version is &lt;strong&gt;25.3.0&lt;/strong&gt; (November 2025), requires Python 3.11+, Apache 2.0 licensed.&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;pip install bluecat-libraries&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;So why did I write my own client?&lt;/strong&gt; The official SDK is the right choice for production systems, but it hides what&#39;s happening over the wire. My examples use raw &lt;code&gt;requests&lt;/code&gt; calls in a thin wrapper so you can see every URL, every JSON body, every header -- which makes it better for learning the API. There&#39;s also no TypeScript/Node.js SDK from BlueCat at all, so the Bun examples in this repo fill that gap.&lt;/p&gt;

&lt;p&gt;Not every project needs the full library either. If you&#39;re writing a small cron job that registers a few DNS records or a provisioning hook that grabs the next available IP for a new VM, a single-file client with just &lt;code&gt;requests&lt;/code&gt; is simpler to drop into an existing project and easier for your team to read without studying SDK docs. For lightweight automation, a thin wrapper is all you need. For production IPAM tooling or multi-API workflows across BAM and DNS Edge, use &lt;code&gt;bluecat-libraries&lt;/code&gt;.&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;Authentication: where it actually breaks&lt;/h2&gt;

&lt;p&gt;If anything trips you up during migration, it&#39;ll be auth. The v1 and v2 approaches have nothing in common.&lt;/p&gt;

&lt;h3&gt;v1 auth (old way)&lt;/h3&gt;

&lt;p&gt;In v1, you send credentials as GET parameters in the URL. Yes, really. And the response is a raw string you have to parse:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v1: Credentials in the URL (!) + string parsing&lt;/span&gt;
&lt;span style=&quot;color:#c586c0;&quot;&gt;import&lt;/span&gt; requests

bam_url &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;https://bam.lab.corp&quot;&lt;/span&gt;
login_url &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{bam_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;/Services/REST/v1/login?username=api&amp;amp;password=pass&quot;&lt;/span&gt;

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Credentials sent as GET parameters -- visible in logs, browser history, proxies&lt;/span&gt;
response &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; requests.get(login_url)

&lt;span style=&quot;color:#6a9955;&quot;&gt;# response.json() returns a plain string in v1, not a dict&lt;/span&gt;
&lt;span style=&quot;color:#6a9955;&quot;&gt;# It looks like: &quot;Session Token-&gt; BAMAuthToken: abc123 ...&quot;&lt;/span&gt;
&lt;span style=&quot;color:#6a9955;&quot;&gt;# You have to split it manually to extract the token&lt;/span&gt;
token &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;str&lt;/span&gt;(response.json())
token &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; token.split()[&lt;span style=&quot;color:#b5cea8;&quot;&gt;2&lt;/span&gt;] &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; token.split()[&lt;span style=&quot;color:#b5cea8;&quot;&gt;3&lt;/span&gt;]

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Then set it as a header for subsequent calls&lt;/span&gt;
header &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; {&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;Authorization&#39;&lt;/span&gt;: token, &lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;application/json&#39;&lt;/span&gt;}

&lt;span style=&quot;color:#6a9955;&quot;&gt;# ... make API calls ...&lt;/span&gt;

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Logout&lt;/span&gt;
requests.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{bam_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;/Services/REST/v1/logout?&quot;&lt;/span&gt;, headers&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;header)&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The obvious problems: your password is in the URL, which means it shows up in server logs, proxy logs, and browser history. The token comes back as a raw string you have to split on whitespace. And if auth fails, you get no structured error -- just a string.&lt;/p&gt;

&lt;h3&gt;v2 auth (new way)&lt;/h3&gt;

&lt;p&gt;In v2, you POST JSON to a sessions endpoint and get back actual JSON:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v2: JSON body + structured response&lt;/span&gt;
&lt;span style=&quot;color:#c586c0;&quot;&gt;import&lt;/span&gt; requests

bam_url &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;https://bam.lab.corp&quot;&lt;/span&gt;

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Credentials sent in POST body -- not in the URL&lt;/span&gt;
resp &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; requests.post(
    &lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{bam_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;/api/v2/sessions&quot;&lt;/span&gt;,
    json&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;{&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;username&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;admin&quot;&lt;/span&gt;, &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;password&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;pass&quot;&lt;/span&gt;},
)
resp.raise_for_status()
data &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; resp.json()

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Token is a clean JSON field -- no string parsing&lt;/span&gt;
token &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; data[&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;apiToken&quot;&lt;/span&gt;]

&lt;span style=&quot;color:#6a9955;&quot;&gt;# The response also includes:&lt;/span&gt;
&lt;span style=&quot;color:#6a9955;&quot;&gt;#   data[&quot;apiTokenExpirationDateTime&quot;] -- when the token expires (~24h)&lt;/span&gt;
&lt;span style=&quot;color:#6a9955;&quot;&gt;#   data[&quot;basicAuthenticationCredentials&quot;] -- pre-encoded base64 for Basic auth&lt;/span&gt;

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Use Bearer auth for subsequent calls&lt;/span&gt;
session &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; requests.Session()
session.headers.update({
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;Bearer &lt;/span&gt;{token}&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&lt;/span&gt;,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;application/json&quot;&lt;/span&gt;,
})

&lt;span style=&quot;color:#6a9955;&quot;&gt;# ... make API calls ...&lt;/span&gt;

&lt;span style=&quot;color:#6a9955;&quot;&gt;# Logout&lt;/span&gt;
session.delete(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{bam_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;/api/v2/sessions&quot;&lt;/span&gt;)&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Credentials stay in the POST body, not the URL. Token is a JSON field you can just read. You also get an expiry timestamp so you know when to refresh, and proper HTTP status codes when something goes wrong. Both Bearer and Basic auth work.&lt;/p&gt;

&lt;h3&gt;Same thing in TypeScript / Bun&lt;/h3&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;// v2 auth in TypeScript (Bun)&lt;/span&gt;
&lt;span style=&quot;color:#569cd6;&quot;&gt;const&lt;/span&gt; resp &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#c586c0;&quot;&gt;await&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;fetch&lt;/span&gt;(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;https://bam.lab.corp/api/v2/sessions&quot;&lt;/span&gt;, {
  method: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;POST&quot;&lt;/span&gt;,
  headers: { &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;application/json&quot;&lt;/span&gt; },
  body: JSON.&lt;span style=&quot;color:#dcdcaa;&quot;&gt;stringify&lt;/span&gt;({ username: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;admin&quot;&lt;/span&gt;, password: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;pass&quot;&lt;/span&gt; }),
});

&lt;span style=&quot;color:#569cd6;&quot;&gt;const&lt;/span&gt; { apiToken } &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#c586c0;&quot;&gt;await&lt;/span&gt; resp.&lt;span style=&quot;color:#dcdcaa;&quot;&gt;json&lt;/span&gt;();

&lt;span style=&quot;color:#6a9955;&quot;&gt;// All subsequent requests use Bearer token&lt;/span&gt;
&lt;span style=&quot;color:#569cd6;&quot;&gt;const&lt;/span&gt; data &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#c586c0;&quot;&gt;await&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;fetch&lt;/span&gt;(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;https://bam.lab.corp/api/v2/configurations&quot;&lt;/span&gt;, {
  headers: { Authorization: &lt;span style=&quot;color:#ce9178;&quot;&gt;`Bearer &lt;/span&gt;${apiToken}&lt;span style=&quot;color:#ce9178;&quot;&gt;`&lt;/span&gt; },
}).&lt;span style=&quot;color:#dcdcaa;&quot;&gt;then&lt;/span&gt;(r &lt;span style=&quot;color:#569cd6;&quot;&gt;=&gt;&lt;/span&gt; r.&lt;span style=&quot;color:#dcdcaa;&quot;&gt;json&lt;/span&gt;());&lt;/code&gt;
&lt;/pre&gt;

&lt;hr/&gt;

&lt;h2&gt;A simple v2 client&lt;/h2&gt;

&lt;p&gt;In the original repo, every script repeated the same boilerplate -- URL construction, token parsing, header building. With v2, you can wrap all of that once and forget about it:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c586c0;&quot;&gt;import&lt;/span&gt; requests

&lt;span style=&quot;color:#569cd6;&quot;&gt;class&lt;/span&gt; &lt;span style=&quot;color:#4ec9b0;&quot;&gt;BAMClient&lt;/span&gt;:
    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;__init__&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;, url, username, password):
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.api_url &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{url.rstrip(&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;/&#39;&lt;/span&gt;)}&lt;span style=&quot;color:#ce9178;&quot;&gt;/api/v2&quot;&lt;/span&gt;
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.session &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; requests.Session()
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.username &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; username
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.password &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; password

    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;login&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;):
        resp &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.session.post(
            &lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.api_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;/sessions&quot;&lt;/span&gt;,
            json&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;{&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;username&quot;&lt;/span&gt;: &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.username, &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;password&quot;&lt;/span&gt;: &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.password},
        )
        resp.raise_for_status()
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.session.headers.update({
            &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;Bearer &lt;/span&gt;{resp.json()[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;apiToken&#39;&lt;/span&gt;]}&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&lt;/span&gt;,
            &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;application/json&quot;&lt;/span&gt;,
        })

    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;logout&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;):
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.session.delete(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.api_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;/sessions&quot;&lt;/span&gt;)

    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;__enter__&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;):
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.login()
        &lt;span style=&quot;color:#c586c0;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;

    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;__exit__&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;, *args):
        &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.logout()

    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;get&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;, path, params&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;None&lt;/span&gt;):
        resp &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.session.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.api_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;&lt;/span&gt;{path}&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&lt;/span&gt;, params&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;params)
        resp.raise_for_status()
        &lt;span style=&quot;color:#c586c0;&quot;&gt;return&lt;/span&gt; resp.json() &lt;span style=&quot;color:#c586c0;&quot;&gt;if&lt;/span&gt; resp.content &lt;span style=&quot;color:#c586c0;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color:#569cd6;&quot;&gt;None&lt;/span&gt;

    &lt;span style=&quot;color:#569cd6;&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;post&lt;/span&gt;(&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;, path, data&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#569cd6;&quot;&gt;None&lt;/span&gt;):
        resp &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.session.post(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;&lt;/span&gt;{&lt;span style=&quot;color:#9cdcfe;&quot;&gt;self&lt;/span&gt;.api_url}&lt;span style=&quot;color:#ce9178;&quot;&gt;&lt;/span&gt;{path}&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&lt;/span&gt;, json&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;data)
        resp.raise_for_status()
        &lt;span style=&quot;color:#c586c0;&quot;&gt;return&lt;/span&gt; resp.json() &lt;span style=&quot;color:#c586c0;&quot;&gt;if&lt;/span&gt; resp.content &lt;span style=&quot;color:#c586c0;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color:#569cd6;&quot;&gt;None&lt;/span&gt;&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Usage:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c586c0;&quot;&gt;with&lt;/span&gt; BAMClient(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;https://bam.example.com&quot;&lt;/span&gt;, &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;admin&quot;&lt;/span&gt;, &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;password&quot;&lt;/span&gt;) &lt;span style=&quot;color:#c586c0;&quot;&gt;as&lt;/span&gt; bam:
    configs &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; bam.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;/configurations&quot;&lt;/span&gt;)
    &lt;span style=&quot;color:#c586c0;&quot;&gt;for&lt;/span&gt; cfg &lt;span style=&quot;color:#c586c0;&quot;&gt;in&lt;/span&gt; configs[&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;data&quot;&lt;/span&gt;]:
        &lt;span style=&quot;color:#dcdcaa;&quot;&gt;print&lt;/span&gt;(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;  [&lt;/span&gt;{cfg[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;id&#39;&lt;/span&gt;]}&lt;span style=&quot;color:#ce9178;&quot;&gt;] &lt;/span&gt;{cfg[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;name&#39;&lt;/span&gt;]}&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&lt;/span&gt;)&lt;/code&gt;
&lt;/pre&gt;

&lt;hr/&gt;

&lt;h2&gt;Endpoint changes, side by side&lt;/h2&gt;

&lt;h3&gt;Adding a host record&lt;/h3&gt;

&lt;p&gt;In v1 you had to walk the hierarchy with &lt;code&gt;getEntityByName&lt;/code&gt; calls, build URLs with string concatenation, and pass everything as query parameters:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v1: Walk Config -&gt; View, then construct URL manually&lt;/span&gt;
getEntityByName &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; mainurl &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;getEntityByName?parentId=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;str&lt;/span&gt;(e_parentId) \
                        &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&amp;amp;name=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; e_name &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&amp;amp;type=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; e_type

addHostRecord &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; mainurl &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;addHostRecord?viewId=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;str&lt;/span&gt;(r_viewId) \
                &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&amp;amp;absoluteName=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; r_absoluteName \
                &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&amp;amp;addresses=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; r_addresses \
                &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&amp;amp;ttl=&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; r_ttl \
                &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&amp;amp;properties=reverseRecord=true&quot;&lt;/span&gt;

response &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; requests.post(addHostRecord, headers&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;header)&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;v2 -- resource paths with JSON bodies. The hierarchy traversal is still there, but it reads like actual REST:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v2: Clean REST resource paths&lt;/span&gt;
&lt;span style=&quot;color:#c586c0;&quot;&gt;with&lt;/span&gt; BAMClient(url, user, password) &lt;span style=&quot;color:#c586c0;&quot;&gt;as&lt;/span&gt; bam:
    config &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; bam.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;/configurations&quot;&lt;/span&gt;, params&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;{&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;filter&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;name:eq(&#39;main&#39;)&quot;&lt;/span&gt;})
    config_id &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; config[&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;data&quot;&lt;/span&gt;][&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;/span&gt;][&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;id&quot;&lt;/span&gt;]

    view &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; bam.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;/configurations/&lt;/span&gt;{config_id}&lt;span style=&quot;color:#ce9178;&quot;&gt;/views&quot;&lt;/span&gt;, params&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;{&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;filter&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;name:eq(&#39;default&#39;)&quot;&lt;/span&gt;})
    view_id &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; view[&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;data&quot;&lt;/span&gt;][&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;/span&gt;][&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;id&quot;&lt;/span&gt;]

    &lt;span style=&quot;color:#6a9955;&quot;&gt;# Find zone, then create host record -- JSON body, not query string&lt;/span&gt;
    zones &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; bam.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;/views/&lt;/span&gt;{view_id}&lt;span style=&quot;color:#ce9178;&quot;&gt;/zones&quot;&lt;/span&gt;)
    zone_id &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; zones[&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;data&quot;&lt;/span&gt;][&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;/span&gt;][&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;id&quot;&lt;/span&gt;]

    bam.post(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;/zones/&lt;/span&gt;{zone_id}&lt;span style=&quot;color:#ce9178;&quot;&gt;/resourceRecords&quot;&lt;/span&gt;, data&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;{
        &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;type&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;HostRecord&quot;&lt;/span&gt;,
        &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;FINRPT02&quot;&lt;/span&gt;,
        &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;addresses&quot;&lt;/span&gt;: [{&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;address&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;192.168.0.16&quot;&lt;/span&gt;}],
        &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;reverseRecord&quot;&lt;/span&gt;: &lt;span style=&quot;color:#569cd6;&quot;&gt;True&lt;/span&gt;,
    })&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;DHCP reservation&lt;/h3&gt;

&lt;p&gt;This one was always painful in v1. The &lt;code&gt;assignNextAvailableIP4Address&lt;/code&gt; call needed host info packed into a comma-delimited string, and properties as pipe-delimited key-value pairs:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v1: Encoding nightmare&lt;/span&gt;
hostInfo &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; hostname &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; zonename &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;,&quot;&lt;/span&gt; &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;str&lt;/span&gt;(viewinfo[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;id&#39;&lt;/span&gt;]) &lt;span style=&quot;color:#d4d4d4;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;,true,false&quot;&lt;/span&gt;
params &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; {
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;configurationId&quot;&lt;/span&gt;: config_id,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;parentId&quot;&lt;/span&gt;: network_id,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;macAddress&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;BB:CC:DD:AA:AA:AA&quot;&lt;/span&gt;,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;hostInfo&quot;&lt;/span&gt;: hostInfo,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;action&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;MAKE_DHCP_RESERVED&quot;&lt;/span&gt;,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;properties&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;name=appsrv23|locationCode=US DAL|&quot;&lt;/span&gt;,
}
response &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; requests.post(assignNextAvailableIP4Addressurl, params&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;params, headers&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;header)&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;v2 -- just JSON:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v2: Just JSON&lt;/span&gt;
bam.post(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;/networks/&lt;/span&gt;{network_id}&lt;span style=&quot;color:#ce9178;&quot;&gt;/nextAvailableAddress&quot;&lt;/span&gt;, data&lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt;{
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;action&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;MAKE_DHCP_RESERVED&quot;&lt;/span&gt;,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;macAddress&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;BB:CC:DD:AA:AA:AA&quot;&lt;/span&gt;,
    &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;name&quot;&lt;/span&gt;: &lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;appsrv23&quot;&lt;/span&gt;,
})&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Server properties: no more pipe parsing&lt;/h3&gt;

&lt;p&gt;This one drove me crazy. In v1, server properties came back as a single pipe-delimited string and you had to parse them into a dict yourself:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v1: Parse pipe-delimited properties manually&lt;/span&gt;
&lt;span style=&quot;color:#c586c0;&quot;&gt;for&lt;/span&gt; server &lt;span style=&quot;color:#c586c0;&quot;&gt;in&lt;/span&gt; serverslist:
    propertieslist &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;list&lt;/span&gt;(server[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;properties&#39;&lt;/span&gt;].split(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;|&quot;&lt;/span&gt;))
    propertiesdic &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; {}
    &lt;span style=&quot;color:#c586c0;&quot;&gt;for&lt;/span&gt; item &lt;span style=&quot;color:#c586c0;&quot;&gt;in&lt;/span&gt; propertieslist:
        &lt;span style=&quot;color:#c586c0;&quot;&gt;if&lt;/span&gt; item &lt;span style=&quot;color:#c586c0;&quot;&gt;is not&lt;/span&gt; &lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;&#39;&lt;/span&gt;:
            shortlist &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;color:#dcdcaa;&quot;&gt;list&lt;/span&gt;(item.split(&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;=&quot;&lt;/span&gt;))
            propertiesdic[shortlist[&lt;span style=&quot;color:#b5cea8;&quot;&gt;0&lt;/span&gt;]] &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; shortlist[&lt;span style=&quot;color:#b5cea8;&quot;&gt;1&lt;/span&gt;]
    server.update(propertiesdic)
    &lt;span style=&quot;color:#c586c0;&quot;&gt;del&lt;/span&gt; server[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;properties&#39;&lt;/span&gt;]&lt;/code&gt;
&lt;/pre&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#6a9955;&quot;&gt;# v2: Properties are just JSON fields. That&#39;s it.&lt;/span&gt;
servers &lt;span style=&quot;color:#d4d4d4;&quot;&gt;=&lt;/span&gt; bam.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;/configurations/&lt;/span&gt;{config_id}&lt;span style=&quot;color:#ce9178;&quot;&gt;/servers&quot;&lt;/span&gt;)
&lt;span style=&quot;color:#c586c0;&quot;&gt;for&lt;/span&gt; server &lt;span style=&quot;color:#c586c0;&quot;&gt;in&lt;/span&gt; servers[&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;data&quot;&lt;/span&gt;]:
    &lt;span style=&quot;color:#dcdcaa;&quot;&gt;print&lt;/span&gt;(&lt;span style=&quot;color:#ce9178;&quot;&gt;f&quot;  &lt;/span&gt;{server[&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;name&#39;&lt;/span&gt;]}&lt;span style=&quot;color:#ce9178;&quot;&gt; - connected: &lt;/span&gt;{server.get(&lt;span style=&quot;color:#ce9178;&quot;&gt;&#39;connected&#39;&lt;/span&gt;)}&lt;span style=&quot;color:#ce9178;&quot;&gt;&quot;&lt;/span&gt;)&lt;/code&gt;
&lt;/pre&gt;

&lt;hr/&gt;

&lt;h2&gt;Office template provisioner&lt;/h2&gt;

&lt;p&gt;I also added something that wasn&#39;t in the original repo at all: a template provisioner. You describe your office in a YAML file -- VLANs, subnets, DHCP pools, hardware MAC addresses, WiFi SSIDs -- and the script creates everything in BAM.&lt;/p&gt;

&lt;p&gt;It runs in dry-run mode by default, so you can see what it would create without a BAM instance:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;$ uv run python examples/07_office_template.py

[DRY RUN] Provisioning: Chicago Office 1 (chi1)
  Domain: chi1.corp.example.com
  Supernet: 10.40.0.0/20

1. Create IP block: 10.40.0.0/20

2. Create networks:
  VLAN  10 | 10.40.0.0/23       | chi1-vlan10-Corp-Data
  VLAN  30 | 10.40.4.0/22       | chi1-vlan30-Corp-WiFi
  VLAN  40 | 10.40.8.0/23       | chi1-vlan40-Guest-WiFi

5. Hardware provisioning:
  switches        | sw-core01            | 10.40.10.129    | AA:BB:CC:01:01:01
  wireless_aps    | ap-1f-01             | 10.40.10.140    | AA:BB:CC:02:01:01
  printers        | pr-1f-01             | 10.40.0.20      | AA:BB:CC:03:01:01

[DRY RUN] Total actions: 25&lt;/code&gt;
&lt;/pre&gt;

&lt;hr/&gt;

&lt;h2&gt;Try it out&lt;/h2&gt;

&lt;h3&gt;Python (uv)&lt;/h3&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;git clone https://github.com/elephantatech/bluecat-bam-v2-examples.git
cd bluecat-bam-v2-examples/python
uv sync
uv run python examples/07_office_template.py&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;TypeScript (Bun)&lt;/h3&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;cd ../nodejs
bun install
bun examples/04-office-template.ts&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;When you have a BAM instance&lt;/h3&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto;&quot;&gt;
&lt;code&gt;export BAM_URL=&quot;https://your-bam.example.com&quot;
export BAM_USER=&quot;admin&quot;
export BAM_PASS=&quot;your-password&quot;
DRY_RUN=false uv run python examples/07_office_template.py&lt;/code&gt;
&lt;/pre&gt;

&lt;hr/&gt;

&lt;h2&gt;The short version&lt;/h2&gt;

&lt;p&gt;Stop using v1. BlueCat calls it &quot;Legacy&quot; now. Auth is POST with JSON instead of credentials in the URL. Responses are real JSON instead of pipe-delimited strings. Endpoints are REST resource paths instead of RPC-style query strings. Every BAM instance has Swagger UI at &lt;code&gt;/api/docs&lt;/code&gt; and an OpenAPI spec at &lt;code&gt;/api/openapi.json&lt;/code&gt; so you can generate clients in any language.&lt;/p&gt;

&lt;p&gt;For production Python, use BlueCat&#39;s official &lt;a href=&quot;https://pypi.org/project/bluecat-libraries/&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;bluecat-libraries&lt;/code&gt;&lt;/a&gt; SDK -- it&#39;s maintained by BlueCat Networks and covers v2, failover, DNS Edge, and Micetro. For lightweight scripts or TypeScript, there&#39;s no official option. The clients in this repo are a good starting point.&lt;/p&gt;

&lt;p&gt;The full project with both Python and TypeScript examples is on GitHub under Apache 2.0: &lt;a href=&quot;https://github.com/elephantatech/bluecat-bam-v2-examples&quot; target=&quot;_blank&quot;&gt;elephantatech/bluecat-bam-v2-examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you spot something wrong or want to add an example, open an issue or PR.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/8071705985059452232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2026/04/migrating-from-bluecat-bam-v1-to-v2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/8071705985059452232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/8071705985059452232'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2026/04/migrating-from-bluecat-bam-v1-to-v2.html' title='Migrating from BlueCat BAM v1 to v2 REST API: Authentication, Code Examples, and What Changed'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-7430240537811321374</id><published>2026-04-23T00:21:58.720-04:00</published><updated>2026-04-23T00:21:58.721-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Animation"/><category scheme="http://www.blogger.com/atom/ns#" term="ASCII Art"/><category scheme="http://www.blogger.com/atom/ns#" term="Developer Experience"/><category scheme="http://www.blogger.com/atom/ns#" term="Game Development"/><category scheme="http://www.blogger.com/atom/ns#" term="Moon Traveler"/><category scheme="http://www.blogger.com/atom/ns#" term="Open Source"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Rich"/><category scheme="http://www.blogger.com/atom/ns#" term="Sprites"/><category scheme="http://www.blogger.com/atom/ns#" term="Terminal"/><category scheme="http://www.blogger.com/atom/ns#" term="Textual"/><category scheme="http://www.blogger.com/atom/ns#" term="TUI"/><category scheme="http://www.blogger.com/atom/ns#" term="UX Design"/><title type='text'>How Bad Animations Made Me Rethink Terminal UX — Moon Traveler v0.5.2</title><content type='html'>&lt;!--
  Blog post for techmystery.blogspot.com
  Title: How Bad Animations Made Me Rethink Terminal UX — Moon Traveler v0.5.2
  Copy the content below into Blogger&#39;s HTML editor
--&gt;

&lt;p&gt;When I shipped v0.5.1 of &lt;a href=&quot;https://github.com/elephantatech/moon_traveler&quot; target=&quot;_blank&quot;&gt;Moon Traveler Terminal&lt;/a&gt;, I thought the narrative intro was enough to make the game feel alive. A flight recorder story. A drone scan report with live stats. Text appearing line by line.&lt;/p&gt;

&lt;p&gt;It wasn&#39;t enough.&lt;/p&gt;

&lt;p&gt;The game still felt like a wall of text. You&#39;d type &lt;code&gt;scan&lt;/code&gt;, get a dump of locations. Type &lt;code&gt;travel&lt;/code&gt;, watch a progress bar crawl. Type &lt;code&gt;talk&lt;/code&gt;, and a creature&#39;s response would just appear. No weight. No presence. The terminal was functional, but it was dead.&lt;/p&gt;

&lt;h3&gt;The Problem Wasn&#39;t Missing Animations — It Was Missing Breath&lt;/h3&gt;

&lt;p&gt;I didn&#39;t realize this until I watched someone else play. They were rushing through commands, barely reading the output. Scan, travel, talk, scan, travel. The game had no rhythm. Every action completed instantly and blurred into the next.&lt;/p&gt;

&lt;p&gt;Terminal games have a unique problem. You don&#39;t have visual cues like camera movement or screen transitions to tell the player &quot;something just happened.&quot; Without those cues, every command feels the same. A hazard event that should be tense gets the same visual treatment as checking your inventory.&lt;/p&gt;

&lt;h3&gt;The First Attempt Was Terrible&lt;/h3&gt;

&lt;p&gt;My first animation attempt was appending frames to the RichLog — the scrollable game output. Each frame was a new line. The scan animation looked like this scrolling down the screen:&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#9aa2c0; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
((.))  ((.))
((.))  ((.))
((.))  ((.))
((.))  ((.))
&lt;/pre&gt;

&lt;p&gt;Four identical lines cluttering the game log. You couldn&#39;t scroll back to read previous output without wading through animation debris. It was worse than no animation at all.&lt;/p&gt;

&lt;p&gt;The fundamental issue: &lt;b&gt;RichLog is append-only&lt;/b&gt;. You can&#39;t update a line in place. Every frame permanently adds to the scroll history.&lt;/p&gt;

&lt;h3&gt;The Fix: A Dedicated 2-Line Widget&lt;/h3&gt;

&lt;p&gt;The breakthrough was adding a &lt;code&gt;Static&lt;/code&gt; widget between the game log and the status bar. Just two lines. That&#39;s it.&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#9aa2c0; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
┌── RichLog (scrollable game output) ──┐
│                                       │
├── &lt;span style=&quot;color:#c89450&quot;&gt;#animation-bar (2 lines, in-place)&lt;/span&gt; ─┤
├── Status Bar (vitals) ────────────────┤
│  Crash Site &gt;  [input]                │
└───────────────────────────────────────┘
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;#animation-bar&lt;/code&gt; uses &lt;code&gt;height: auto; max-height: 2&lt;/code&gt; in the Textual CSS. When empty, it collapses to zero height — invisible. When an animation plays, it expands to show the sprite. When done, it collapses again. The game log never gets polluted.&lt;/p&gt;

&lt;p&gt;The CSS is dead simple:&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5ca8c8&quot;&gt;#animation-bar&lt;/span&gt; {
    &lt;span style=&quot;color:#c89450&quot;&gt;height&lt;/span&gt;: auto;
    &lt;span style=&quot;color:#c89450&quot;&gt;max-height&lt;/span&gt;: 2;
}
&lt;/pre&gt;

&lt;p&gt;Every animation function follows the same pattern:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Check if animations are enabled (config + runtime flag)&lt;/li&gt;
  &lt;li&gt;Check if the animation widget exists (TUI bridge present)&lt;/li&gt;
  &lt;li&gt;Play frames in place, each replacing the previous&lt;/li&gt;
  &lt;li&gt;Hold the last frame so the player can read it&lt;/li&gt;
  &lt;li&gt;Clear the widget&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If animations are disabled, fall back to a simple text message. No crash, no empty space.&lt;/p&gt;

&lt;h3&gt;Why Timing Is Everything&lt;/h3&gt;

&lt;p&gt;Here&#39;s the thing nobody tells you about terminal animations: &lt;b&gt;the frames don&#39;t matter nearly as much as the pauses between them.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;My first scan animation used a 0.15-second delay between frames. It was a blur. You couldn&#39;t even see the radar spinning. Bumped it to 0.35 seconds and suddenly it felt like the drone was actually scanning something. The player&#39;s eye had time to register each frame.&lt;/p&gt;

&lt;p&gt;But timing isn&#39;t just about animation speed. It&#39;s about the gaps &lt;i&gt;between&lt;/i&gt; actions.&lt;/p&gt;

&lt;p&gt;I added a &lt;code&gt;beat()&lt;/code&gt; function — a simple &lt;code&gt;time.sleep(0.8)&lt;/code&gt; after every valid command. No visual output. Just a pause. That tiny breath between &quot;you typed something&quot; and &quot;the game responds to the next thing&quot; changed the entire feel. It gave each action weight.&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#9aa2c0; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5ac870&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#c89450&quot;&gt;beat&lt;/span&gt;(duration: float = 0.8):
    &lt;span style=&quot;color:#5a6280&quot;&gt;&quot;&quot;&quot;Pause to let the player absorb output.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span style=&quot;color:#5ac870&quot;&gt;if not&lt;/span&gt; _enabled():
        &lt;span style=&quot;color:#5ac870&quot;&gt;return&lt;/span&gt;
    time.sleep(duration)
&lt;/pre&gt;

&lt;p&gt;Here&#39;s what the game flow looks like with proper timing:&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#9aa2c0; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5ca8c8&quot;&gt;Crash Site &gt;&lt;/span&gt; scan
  &lt;span style=&quot;color:#5a6280&quot;&gt;[scan animation — 0.35s per frame]&lt;/span&gt;
  &lt;span style=&quot;color:#5a6280&quot;&gt;[results appear]&lt;/span&gt;
  &lt;span style=&quot;color:#5a6280&quot;&gt;[beat — 0.8s pause]&lt;/span&gt;
&lt;span style=&quot;color:#5ca8c8&quot;&gt;Crash Site &gt;&lt;/span&gt; travel Frost Ridge
  &lt;span style=&quot;color:#c478d0&quot;&gt;[drone flies across the field]&lt;/span&gt;
  &lt;span style=&quot;color:#5ac870&quot;&gt;Arrived at Frost Ridge.&lt;/span&gt;
  &lt;span style=&quot;color:#5a6280&quot;&gt;[beat]&lt;/span&gt;
&lt;span style=&quot;color:#5ca8c8&quot;&gt;Frost Ridge &gt;&lt;/span&gt; look
  &lt;span style=&quot;color:#5a6280&quot;&gt;[eyes scan left to right — 0.35s]&lt;/span&gt;
  &lt;span style=&quot;color:#5a6280&quot;&gt;[location description]&lt;/span&gt;
  &lt;span style=&quot;color:#5a6280&quot;&gt;[beat]&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Without those beats, the output runs together. With them, each command feels like a distinct moment. The player naturally slows down and reads what&#39;s on screen.&lt;/p&gt;

&lt;h3&gt;The Drone That Grows With You&lt;/h3&gt;

&lt;p&gt;The most satisfying animation to build was the travel drone. It&#39;s a 2-line ASCII sprite that moves across the animation bar as you travel between locations.&lt;/p&gt;

&lt;p&gt;The drone evolves with upgrades. Here&#39;s the full progression:&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#c478d0; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5a6280&quot;&gt;Base drone (no upgrades):&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  [ ]--(+)--[ ]&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  \___________/&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;After 1 upgrade (range module):&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  [O]--(+)--[O]&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  \[]_________/&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;After 2 upgrades:&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  [O]--(+)--[O]&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  \[]_______[]/&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;After 3 upgrades:&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  [O]--(+)--[O]&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  \[][]_____[]/&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;After 4 upgrades:&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  [O]--(+)--[O]&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;  \[][]___[][]/&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;FULLY UPGRADED (5 modules):&lt;/span&gt;
&lt;span style=&quot;color:#c89450&quot;&gt;  [O]--(+)--[O]&lt;/span&gt;
&lt;span style=&quot;color:#c89450&quot;&gt;  \[][][]_[][]/&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;The eyes change from empty to &lt;code&gt;O&lt;/code&gt; with any upgrade — the drone &quot;wakes up.&quot; The belly fills with &lt;code&gt;[]&lt;/code&gt; pairs as you install modules. Each pair fills from the edges inward, creating a visual symmetry.&lt;/p&gt;

&lt;h3&gt;The Debugging Adventures&lt;/h3&gt;

&lt;p&gt;This was tricky to get right. Two bugs that cost me hours:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Rich eating the eyes.&lt;/b&gt; The terminal rendering library, Rich, interprets &lt;code&gt;[o]&lt;/code&gt; as a markup style tag. The drone&#39;s eyes would vanish. Every &lt;code&gt;[&lt;/code&gt; in the sprite needs escaping as &lt;code&gt;\[&lt;/code&gt;. But &lt;code&gt;]&lt;/code&gt; does NOT need escaping — adding &lt;code&gt;\]&lt;/code&gt; makes a literal backslash appear. I learned this the hard way.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The drunk drone.&lt;/b&gt; The top line was 13 characters. The bottom was 12. Off by one. As the drone moved across the screen, the bottom line would drift to the left, making it look like it was flying sideways. I expanded the belly from 9 to 11 characters to match — both lines exactly 13 visible characters.&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5a6280&quot;&gt;# The alignment that took an entire session to debug:&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;[O]--(+)--[O]&lt;/span&gt;   &lt;span style=&quot;color:#5a6280&quot;&gt;← 13 chars&lt;/span&gt;
&lt;span style=&quot;color:#c478d0&quot;&gt;\___________/&lt;/span&gt;   &lt;span style=&quot;color:#5a6280&quot;&gt;← 13 chars (\ + 11 belly + /)&lt;/span&gt;
&lt;/pre&gt;

&lt;h3&gt;Late-Game Tension Through Animation&lt;/h3&gt;

&lt;p&gt;After 24 in-game hours, the animations shift. The scan radar picks up interference frames. Hazard flashes use triple exclamation marks. The travel animation occasionally glitches with interference patterns.&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#9aa2c0; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5a6280&quot;&gt;Normal scan:&lt;/span&gt;      &lt;span style=&quot;color:#5ca8c8&quot;&gt;((*))  ((*))&lt;/span&gt;
&lt;span style=&quot;color:#5a6280&quot;&gt;Late-game scan:&lt;/span&gt;   &lt;span style=&quot;color:#c89450&quot;&gt;((!))  ((!))&lt;/span&gt;   &lt;span style=&quot;color:#5a6280&quot;&gt;← interference&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;Normal hazard:&lt;/span&gt;    &lt;span style=&quot;color:#c85050&quot;&gt;/!\&lt;/span&gt;
                  &lt;span style=&quot;color:#c85050&quot;&gt;— HAZARD —&lt;/span&gt;

&lt;span style=&quot;color:#5a6280&quot;&gt;Late-game hazard:&lt;/span&gt; &lt;span style=&quot;color:#c85050&quot;&gt;/!\&lt;/span&gt;
                  &lt;span style=&quot;color:#c85050&quot;&gt;!!! HAZARD !!!&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;It&#39;s a small touch, but it reinforces the narrative: the environment is deteriorating. The longer you survive, the more hostile Enceladus becomes. The animations tell that story without a single word of dialogue.&lt;/p&gt;

&lt;h3&gt;The Animation System Architecture&lt;/h3&gt;

&lt;p&gt;The whole system is one file: &lt;code&gt;animations.py&lt;/code&gt;, 250 lines. Here&#39;s the gate pattern every function uses:&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5ac870&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#c89450&quot;&gt;_can_animate&lt;/span&gt;() -&gt; bool:
    &lt;span style=&quot;color:#5a6280&quot;&gt;&quot;&quot;&quot;Check if the animation widget is available.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span style=&quot;color:#5ac870&quot;&gt;return&lt;/span&gt; _enabled() &lt;span style=&quot;color:#5ac870&quot;&gt;and&lt;/span&gt; hasattr(ui.console, &lt;span style=&quot;color:#c89450&quot;&gt;&quot;animate_frame&quot;&lt;/span&gt;)

&lt;span style=&quot;color:#5ac870&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#c89450&quot;&gt;scan_sweep&lt;/span&gt;(hours_elapsed=0):
    &lt;span style=&quot;color:#5ac870&quot;&gt;if not&lt;/span&gt; _can_animate():
        ui.console.print(&lt;span style=&quot;color:#c89450&quot;&gt;&quot;  Scanning surroundings...&quot;&lt;/span&gt;)
        time.sleep(0.5)
        &lt;span style=&quot;color:#5ac870&quot;&gt;return&lt;/span&gt;
    &lt;span style=&quot;color:#5a6280&quot;&gt;# ... play animated version ...&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;_can_animate()&lt;/code&gt; gate checks two things: is the animation system enabled (config setting + runtime flag), and does the TUI bridge exist. This means animations work in the full TUI, gracefully degrade in headless mode, and can be toggled with &lt;code&gt;--disable-animation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thread safety comes from Textual&#39;s &lt;code&gt;call_from_thread&lt;/code&gt;. The game logic runs in a worker thread, but widget updates happen on the main thread. The bridge handles the crossing:&lt;/p&gt;

&lt;pre style=&quot;background:#0c1020; color:#d4d4d4; padding:16px; border-radius:6px; overflow-x:auto; font-family:&#39;JetBrains Mono&#39;,monospace; font-size:14px;&quot;&gt;
&lt;span style=&quot;color:#5ac870&quot;&gt;def&lt;/span&gt; &lt;span style=&quot;color:#c89450&quot;&gt;animate_frame&lt;/span&gt;(self, text):
    bar = getattr(self._app, &lt;span style=&quot;color:#c89450&quot;&gt;&quot;_animation_bar&quot;&lt;/span&gt;, None)
    &lt;span style=&quot;color:#5ac870&quot;&gt;if&lt;/span&gt; bar:
        self._app.call_from_thread(bar.update, text)
&lt;/pre&gt;

&lt;h3&gt;What I Learned&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;b&gt;Two lines is enough.&lt;/b&gt; You don&#39;t need elaborate terminal graphics. A 2-line widget that appears and disappears adds more life than any amount of scrolling text art.&lt;/li&gt;

  &lt;li&gt;&lt;b&gt;Timing matters more than frames.&lt;/b&gt; A well-timed pause does more than a complex sprite. The &lt;code&gt;beat()&lt;/code&gt; function is literally just &lt;code&gt;time.sleep(0.8)&lt;/code&gt; and it transformed the game&#39;s pacing.&lt;/li&gt;

  &lt;li&gt;&lt;b&gt;In-place updates beat scrolling.&lt;/b&gt; Animations that append to the scroll history are worse than no animations. Dedicated widgets that update in place are the way.&lt;/li&gt;

  &lt;li&gt;&lt;b&gt;Always hold the last frame.&lt;/b&gt; 0.6 seconds before clearing. If the animation vanishes instantly, the player&#39;s brain never registers it happened.&lt;/li&gt;

  &lt;li&gt;&lt;b&gt;Fallback gracefully.&lt;/b&gt; Every animation checks if the widget exists before playing. If it doesn&#39;t, print a simple message. Never crash, never leave blank space.&lt;/li&gt;

  &lt;li&gt;&lt;b&gt;Let the drone grow.&lt;/b&gt; Cosmetic progression that reflects real gameplay changes — the belly filling with modules — gives the player visible proof their effort matters. Even in ASCII.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The game went from a wall of text to something that breathes. Every scan feels like a scan. Every trip feels like a journey. Every hazard feels dangerous. All it took was two lines of screen space and some carefully tuned &lt;code&gt;time.sleep()&lt;/code&gt; calls.&lt;/p&gt;

&lt;hr style=&quot;border:none; border-top:1px solid #333; margin:2rem 0;&quot;&gt;

&lt;p&gt;&lt;i&gt;Moon Traveler Terminal v0.5.2 is out now — ASCII animations, drone sprite evolution, in-place upgrades, and LLM performance diagnostics.&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;
  &lt;a href=&quot;https://github.com/elephantatech/moon_traveler&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; |
  &lt;a href=&quot;https://elephantatech.github.io/moon_traveler/v052.html&quot; target=&quot;_blank&quot;&gt;Release Notes&lt;/a&gt; |
  &lt;a href=&quot;https://elephantatech.github.io/moon_traveler/releases.html&quot; target=&quot;_blank&quot;&gt;All Releases&lt;/a&gt;
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/7430240537811321374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2026/04/how-bad-animations-made-me-rethink.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7430240537811321374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7430240537811321374'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2026/04/how-bad-animations-made-me-rethink.html' title='How Bad Animations Made Me Rethink Terminal UX — Moon Traveler v0.5.2'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-1814661957311245857</id><published>2026-04-21T14:49:00.004-04:00</published><updated>2026-04-24T13:53:28.712-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ai"/><category scheme="http://www.blogger.com/atom/ns#" term="architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="development"/><category scheme="http://www.blogger.com/atom/ns#" term="LLM"/><category scheme="http://www.blogger.com/atom/ns#" term="opensource"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Terminal"/><category scheme="http://www.blogger.com/atom/ns#" term="Textual"/><category scheme="http://www.blogger.com/atom/ns#" term="TUI"/><title type='text'>Automated Screenshot Testing for a Python Terminal Game</title><content type='html'>&lt;!--
  Blog post for techmystery.blogspot.com
  Title: Automated Screenshot Testing for a Python Terminal Game
  Copy the content below into Blogger&#39;s HTML editor
--&gt;

&lt;p&gt;I&#39;ve been building a terminal-based survival game in Python using the &lt;a href=&quot;https://textual.textualize.io/&quot; target=&quot;_blank&quot;&gt;Textual&lt;/a&gt; framework. It runs as a TUI (Terminal User Interface) with Rich markup, threaded game logic, and SQLite for persistence. The game is called &lt;a href=&quot;https://github.com/elephantatech/moon_traveler&quot; target=&quot;_blank&quot;&gt;Moon Traveler Terminal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The problem: I needed automated screenshots for documentation, GitHub Pages, and regression testing. Taking them manually every release was not sustainable. So I built a pilot script that plays the entire game, captures 27 screenshots at key moments, and validates the output.&lt;/p&gt;

&lt;p&gt;Here&#39;s what I learned and the problems I had to solve.&lt;/p&gt;

&lt;h3&gt;The Architecture Challenge&lt;/h3&gt;

&lt;p&gt;The game has a split-thread design:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;b&gt;Worker thread&lt;/b&gt; - game logic, LLM inference, time.sleep() for animations&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Main thread&lt;/b&gt; - Textual UI rendering, input handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every &lt;code&gt;print()&lt;/code&gt; call in the game routes through a thread-safe bridge to Textual&#39;s &lt;code&gt;RichLog&lt;/code&gt; widget:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
class UIBridge:
    def print(self, *args, **kwargs):
        # Route to Textual&#39;s RichLog via call_from_thread
        self._app.call_from_thread(self._log.write, args[0])
&lt;/pre&gt;

&lt;p&gt;Each print is immediately visible in the UI. No buffering. This is great for the player but tricky for automated testing - you never know exactly when output finishes rendering.&lt;/p&gt;

&lt;h3&gt;Step 1: Textual&#39;s Auto-Pilot&lt;/h3&gt;

&lt;p&gt;Textual has a built-in test harness. You pass an &lt;code&gt;auto_pilot&lt;/code&gt; coroutine to &lt;code&gt;app.run()&lt;/code&gt; and it drives the UI programmatically. No real terminal needed.&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
from src.tui_app import MoonTravelerApp

async def screenshot_pilot(pilot):
    app = pilot.app

    async def take(name, desc):
        await pilot.pause(0.5)
        app.refresh()
        await pilot.pause(0.5)
        app.save_screenshot(f&quot;assets/{name}.svg&quot;)

    async def send(text, wait=4.0):
        app.command_queue.put(text)
        await pilot.pause(wait)

    # Take title screenshot
    await pilot.pause(3.0)
    await take(&quot;tui-title&quot;, &quot;Title screen&quot;)

    # Play the game
    await send(&quot;look&quot;, wait=3.0)
    await take(&quot;tui-look&quot;, &quot;Look at crash site&quot;)

    await send(&quot;scan&quot;, wait=3.0)
    await take(&quot;tui-scan&quot;, &quot;Scan results&quot;)

app = MoonTravelerApp()
app.run(auto_pilot=screenshot_pilot)
&lt;/pre&gt;

&lt;p&gt;Three ways to talk to the game:&lt;/p&gt;

&lt;table border=&quot;1&quot; cellpadding=&quot;6&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse; margin:12px 0;&quot;&gt;
  &lt;tr style=&quot;background:#333;&quot;&gt;&lt;td&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;What it does&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;&lt;code&gt;command_queue.put(text)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Injects a command (like typing + Enter)&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;&lt;code&gt;bridge.push_response(text)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Answers interactive prompts (y/n, menus)&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;&lt;code&gt;wait_for_ask_mode()&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Polls until the game blocks on a prompt&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;h3&gt;Step 2: Handling Branching Game Flows&lt;/h3&gt;

&lt;p&gt;The game has branching prompts - new game vs load, difficulty selection, player name. You cannot just hardcode sleep timers. You have to wait for the game to actually ask a question.&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
async def wait_for_ask_mode(timeout=10.0):
    &quot;&quot;&quot;Wait until the game blocks on a prompt.&quot;&quot;&quot;
    elapsed = 0.0
    while elapsed &lt; timeout:
        if app._ask_mode:
            return True
        await pilot.pause(0.3)
        elapsed += 0.3
    return False

# Navigate: New Game -&gt; Easy -&gt; Player name
if await wait_for_ask_mode(timeout=5.0):
    await respond(&quot;1&quot;, wait=2.0)      # &quot;New Game&quot;
if await wait_for_ask_mode(timeout=5.0):
    await respond(&quot;1&quot;, wait=2.0)      # &quot;Easy&quot; difficulty
if await wait_for_ask_mode(timeout=5.0):
    await respond(&quot;Screenshot&quot;, wait=3.0)  # Player name
&lt;/pre&gt;

&lt;p&gt;This pattern made the script reliable across different game states - fresh install with no saves, existing saves, different model loading times.&lt;/p&gt;

&lt;h3&gt;Problem 1: Screenshots Only Capture the Viewport&lt;/h3&gt;

&lt;p&gt;This was the first real surprise. Textual&#39;s &lt;code&gt;save_screenshot()&lt;/code&gt; exports what&#39;s currently &lt;b&gt;visible&lt;/b&gt; in the viewport - about 24 lines. Content that scrolled off the top is gone from the SVG.&lt;/p&gt;

&lt;p&gt;I built a narrative intro that&#39;s 18 lines long. By the time the boot sequence finishes, the heading at the top has scrolled away. My first validation checked for &quot;FLIGHT RECORDER&quot; in the screenshot - it failed because the heading was above the viewport.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The fix:&lt;/b&gt; Always validate against text near the &lt;b&gt;bottom&lt;/b&gt; of each screen, not the top.&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
import re

def _svg_text(path):
    &quot;&quot;&quot;Extract visible text from an SVG screenshot.&quot;&quot;&quot;
    with open(path) as f:
        return &quot; &quot;.join(
            t.replace(&quot;&amp;amp;#160;&quot;, &quot; &quot;).strip()
            for t in re.findall(r&quot;&amp;gt;([^&amp;lt;]+)&amp;lt;&quot;, f.read())
            if t.strip() and len(t.strip()) &amp;gt; 2
        )

# Validate bottom-of-screen content, not headers
validations = [
    (&quot;tui-intro&quot;, &quot;rescue&quot;, &quot;Intro narrative visible&quot;),
    (&quot;tui-help&quot;, &quot;drone&quot;, &quot;Help shows commands&quot;),
    (&quot;tui-victory&quot;, &quot;Grade&quot;, &quot;Victory shows score&quot;),
    (&quot;tui-scores&quot;, &quot;Ripley&quot;, &quot;Leaderboard has entries&quot;),
]

for name, expected, desc in validations:
    text = _svg_text(f&quot;assets/{name}.svg&quot;)
    status = &quot;PASS&quot; if expected.lower() in text.lower() else &quot;FAIL&quot;
    print(f&quot;  {status}: {desc}&quot;)
&lt;/pre&gt;

&lt;h3&gt;Problem 2: SQLite Data Pollution&lt;/h3&gt;

&lt;p&gt;The screenshot script seeds fake leaderboard entries so the scores screenshot is not empty:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
from src.save_load import record_score

record_score(820, &quot;A&quot;, True, &quot;short&quot;, 18, 1200, 3, 12345,
             player_name=&quot;Ripley&quot;)
record_score(650, &quot;B&quot;, True, &quot;medium&quot;, 35, 2400, 2, 67890,
             player_name=&quot;Dallas&quot;)
record_score(410, &quot;C&quot;, False, &quot;long&quot;, 12, 900, 1, 11111,
             player_name=&quot;Lambert&quot;)
&lt;/pre&gt;

&lt;p&gt;The problem: these entries persisted across runs. After running the script 5 times, I had 15 fake entries in my real database mixed with actual play data.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The fix:&lt;/b&gt; Clean up test data on exit, keyed by player name:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
import sqlite3

# Always clean up, even if the script crashes elsewhere
with sqlite3.connect(str(db_path)) as conn:
    conn.execute(
        &quot;DELETE FROM leaderboard WHERE player_name &quot;
        &quot;IN (&#39;Ripley&#39;, &#39;Dallas&#39;, &#39;Lambert&#39;, &#39;Screenshot&#39;)&quot;
    )
&lt;/pre&gt;

&lt;p&gt;I also added state logging at every screenshot checkpoint - player location, inventory, repair progress, and full DB row counts. When a screenshot looks wrong, the debug log tells you exactly what the game state was at capture time:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
def log_game_state(ctx, label=&quot;&quot;):
    p = ctx.player
    log(f&quot;[{label}] Loc={p.location_name}&quot;)
    log(f&quot;[{label}] Food={p.food:.0f}% Water={p.water:.0f}%&quot;)
    log(f&quot;[{label}] Inventory={dict(p.inventory)}&quot;)

def log_db_state(label=&quot;&quot;):
    with sqlite3.connect(str(db_path)) as conn:
        for table in [&quot;saves&quot;, &quot;chat_history&quot;, &quot;leaderboard&quot;]:
            n = conn.execute(f&quot;SELECT COUNT(*) FROM [{table}]&quot;).fetchone()[0]
            log(f&quot;[DB {label}] {table}: {n} rows&quot;)
&lt;/pre&gt;

&lt;h3&gt;Problem 3: Animations Break Script Timing&lt;/h3&gt;

&lt;p&gt;I added ASCII frame animations to the game - scan sweeps, travel progress bars, hazard flashes. Each one adds 0.3 to 1.0 seconds of &lt;code&gt;time.sleep()&lt;/code&gt; in the worker thread. The screenshot script&#39;s fixed &lt;code&gt;await pilot.pause(3.0)&lt;/code&gt; durations were suddenly too tight.&lt;/p&gt;

&lt;p&gt;The wrong fix would be to disable animations in the config file - that persists and would turn off animations for the user&#39;s next real play session.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The right fix:&lt;/b&gt; A runtime kill switch that only lasts for the current process:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
# src/animations.py
_force_disabled = False

def force_disable():
    &quot;&quot;&quot;Session-only. Does NOT persist to config.&quot;&quot;&quot;
    global _force_disabled
    _force_disabled = True

def _enabled():
    if _force_disabled:
        return False
    from src.config import get_animations_enabled
    return get_animations_enabled()
&lt;/pre&gt;

&lt;p&gt;The game&#39;s &lt;code&gt;--super&lt;/code&gt; mode (used by test scripts) calls &lt;code&gt;force_disable()&lt;/code&gt; at startup. Real players still get animations. Test scripts get deterministic timing.&lt;/p&gt;

&lt;h3&gt;Problem 4: Capturing the Game Context&lt;/h3&gt;

&lt;p&gt;The screenshot script needs access to the live game state (player location, creatures, inventory) to make smart decisions - like finding a creature to talk to. But the game context only exists inside the worker thread.&lt;/p&gt;

&lt;p&gt;Solution: monkey-patch the game loop to capture the context object:&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
import threading
from src import game

_game_ctx = None
_ctx_ready = threading.Event()
_original_game_loop = game.game_loop

def _patched_game_loop(ctx):
    global _game_ctx
    _game_ctx = ctx
    _ctx_ready.set()        # Signal that context is ready
    return _original_game_loop(ctx)

game.game_loop = _patched_game_loop

# Later in the pilot:
_ctx_ready.wait(timeout=30)
ctx = _game_ctx

# Now we can query live game state
creature_loc = None
for c in ctx.creatures:
    if c.location_name in ctx.player.known_locations:
        creature_loc = c.location_name
        break
&lt;/pre&gt;

&lt;h3&gt;The End Result&lt;/h3&gt;

&lt;p&gt;The full script plays an entire game: new game, explore, scan, travel to creatures, have LLM-powered conversations, escort allies back to the ship, repair and win. 27 screenshots, 10 validated, all in about 3 minutes.&lt;/p&gt;

&lt;pre style=&quot;background:#1e1e1e; color:#d4d4d4; padding:12px; border-radius:4px; overflow-x:auto;&quot;&gt;
$ uv run python scripts/tui_screenshots.py

Taking TUI screenshots...
  Saved: assets/tui-title.svg — Title screen
  Saved: assets/tui-intro.svg — Flight recorder narrative
  Saved: assets/tui-crash-site.svg — Crash site after boot
  Saved: assets/tui-look.svg — Look at crash site
  ...
  Saved: assets/tui-victory.svg — Victory screen
Validation: 10 passed, 0 failed
  Cleaned up seeded leaderboard entries
Done! Screenshots saved to assets/
&lt;/pre&gt;

&lt;h3&gt;Lessons Learned&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;b&gt;Textual&#39;s auto-pilot is powerful&lt;/b&gt; but you need polling patterns like &lt;code&gt;wait_for_ask_mode()&lt;/code&gt; for branching flows. Fixed sleeps alone will not work.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Viewport screenshots miss scrollback.&lt;/b&gt; Validate against content near the bottom of the screen, never the top.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Clean up test data.&lt;/b&gt; If your script seeds a database, delete those rows on exit. Key by a known player name so you can always find them.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Animations need a runtime kill switch&lt;/b&gt; for automated scripts. Never persist test-only config changes.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Log game state at capture time.&lt;/b&gt; When a screenshot fails validation, you need the context - not just a failed assertion.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Monkey-patching the game loop&lt;/b&gt; to capture the context object is ugly but effective. It lets the pilot script make decisions based on live game state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The game and all the testing scripts are open source:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/elephantatech/moon_traveler&quot; target=&quot;_blank&quot;&gt;https://github.com/elephantatech/moon_traveler&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://elephantatech.github.io/moon_traveler/&quot; target=&quot;_blank&quot;&gt;https://elephantatech.github.io/moon_traveler/&lt;/a&gt;&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/1814661957311245857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2026/04/automated-screenshot-testing-for-python.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1814661957311245857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1814661957311245857'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2026/04/automated-screenshot-testing-for-python.html' title='Automated Screenshot Testing for a Python Terminal Game'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-1356740860594380043</id><published>2016-06-29T16:49:00.000-04:00</published><updated>2016-06-29T16:49:02.564-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="appliance"/><category scheme="http://www.blogger.com/atom/ns#" term="Cisco"/><category scheme="http://www.blogger.com/atom/ns#" term="custom"/><category scheme="http://www.blogger.com/atom/ns#" term="customize"/><category scheme="http://www.blogger.com/atom/ns#" term="google"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="reasons"/><category scheme="http://www.blogger.com/atom/ns#" term="security"/><category scheme="http://www.blogger.com/atom/ns#" term="windows"/><title type='text'>5 Reasons for not Customizing Appliance Devices</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
&lt;b&gt;5 Reasons for not Customizing Appliance Devices&lt;/b&gt;&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
As an IT Support Specialist I have seen many things that other support professionals do things on their environments that is not advised or suggested from support perspective. Appliance servers are servers however with a key different they are supposed to run only one software. Example would be like the google appliance or networking devices like the router switches from Cisco. This devices run on an OS that is customized for this purpose only so they do not run standard settings. Some System Administrator might want to run other software to save cost or play around or simply need to for what ever legitimate or illegitimate reason.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Here are 5 reason for not customizing appliance servers&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;
Reason 1 : Break something that is critical for appliance Software&lt;/h3&gt;
&lt;div&gt;
Appliance software is created for a purpose to be hosted on that appliance. So the software and hardware is designed for that in mind. So before trying to installing something or configuring something always check with vendors Support. Do not go to consultants only. There are some fantastic consultants out there but you want to make sure you know your options. Examples are like upgrading java to the latest version on an appliance that requires to have particular version or the appliance software will not work.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;
Reason 2 : unable to fix problems/upgrade the appliance software due to custom configuration&lt;/h3&gt;
&lt;div&gt;
Sometimes the upgrade path breaks on the appliance because someone decided that they needed to customize the configuration to allow other things to run. Well you might just block the upgrade path to the latest version. Sometimes it is the vendors fault here but most of the time when you customize the software too much you will not able to upgrade you will have to migrate instead so you will have to spend more money to get a new appliance with newer software. So you will not get the same support from vendors for technical issues.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;
Reason 3 : Utilizing resources that are needed for appliance software&lt;/h3&gt;
&lt;div&gt;
As previously stated the hardware and software on an appliance are created for a specific task and configuration. If you add more services to run outside of the box you run into the problem like lack of hard-drive or over utilizing RAM or over utilizing the network card. You are dealing with finite resources and the device is tested with thousands and sometimes even millions of dollars to do just what it was designed to do. Hacking it out to run multiple services will slow everything down including what the appliance was designed for. Performance is one reason you want a separate appliance if you start hacking to add more services that that was not intended for the device you just threw out an advantage that you had with the appliance.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;
Reason 4 : Warranty issues with vendors&lt;/h3&gt;
&lt;div&gt;
Most vendors will only give best effort in some cases will void the warranty outright so when you need help when you call the vendor, they will not give the full support that you need. You have hacked and customized things that they are not trained or even experienced in so now good luck trying to get that quick fix you were hoping for since the Support tech first needs to learn what you did then learn how he can do it then see if that is supported. and If it is out of the warranty or paid support agreement well you are out of luck and you might even have to pay more for assistance now.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
Reason 5 : Security loophole&lt;/h3&gt;
&lt;div&gt;
What happens when you install software it can open ports that now increase the risk for a security breach. If you know someone in security they will explain this with more services running means more chances of security breach as you increase risk. It is simple if the device what security tested, only allows some services to use those don&#39;t add more. for example don&#39;t install ftp server on a network device because ftp is weak. You need SFTP or SCP instead however if the device does not support it, don&#39;t add any of that since you will be opening ports that are a risk to the network now, and not just the device.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Don&#39;t get me wrong sometimes the default configuration can have a security risk or have something that does not work for your enterprise network so It is a good idea to customize and hack out the appliance. Just know what you are getting into talk to your consultants and your vendor Support. You want to talk to Support specifically because they know what can go wrong with a custom configuration or can get the information what the risks are by adding a customization.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/1356740860594380043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2016/06/5-reasons-for-not-customizing-appliance.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1356740860594380043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1356740860594380043'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2016/06/5-reasons-for-not-customizing-appliance.html' title='5 Reasons for not Customizing Appliance Devices'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-7262865514473198562</id><published>2016-06-17T09:21:00.002-04:00</published><updated>2016-06-17T09:31:36.792-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="command"/><category scheme="http://www.blogger.com/atom/ns#" term="find"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><title type='text'>Search Large files on Linux</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;br /&gt;
&lt;br /&gt;
Ever wonder how to find large files in Linux but you have well there is the find command you can use here some examples I found really helpful.&lt;br /&gt;
&lt;br /&gt;
To find files larger than 100MB:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;find . -type f -size +100M&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
If you want the current dir only:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;find . -maxdepth 1 -type f -size +100M&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
If you wish to see all files over 100M and to see where they are and what is their size try this:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;find . -type f -size +100M -exec ls -lh {} \;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
If you wish to check all the files in the system then run the command from the system root (/) directory with sudo.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;cd \&lt;br /&gt;sudo find . -type f -size +100M -exec ls -lh {} \;&lt;/code&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/7262865514473198562/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2016/06/search-large-files-on-linux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7262865514473198562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7262865514473198562'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2016/06/search-large-files-on-linux.html' title='Search Large files on Linux'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-2208836122105293651</id><published>2015-01-26T12:22:00.001-05:00</published><updated>2015-01-28T14:40:35.680-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="2015"/><category scheme="http://www.blogger.com/atom/ns#" term="2015 ride for heart"/><category scheme="http://www.blogger.com/atom/ns#" term="becel"/><category scheme="http://www.blogger.com/atom/ns#" term="heartandstroke"/><category scheme="http://www.blogger.com/atom/ns#" term="ride for heart"/><title type='text'>Becel Heart&amp;Stroke Ride for Heart 2015</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Every &lt;b&gt;7 minutes &lt;/b&gt;someone dies from &lt;b&gt;heart disease and stroke in Canada.&lt;/b&gt; That&#39;s why I am fundraising for the &lt;b&gt;Becel Heart&amp;amp;Stroke Ride for Heart &lt;/b&gt;on &lt;b&gt;Sunday, May 31, 2015&lt;/b&gt; to support heart and stroke foundation. However I am also doing this for 2 other reasons. I want to ride my bicycle on the Don Valley Parkway and I want to get into shape.&lt;br /&gt;
&lt;br /&gt;
First I would&amp;nbsp;like&amp;nbsp;to talk about the Heart and Stroke foundation which has contributed a lot for Canadians to&amp;nbsp;support treatment and assist in prevention of heart attacks and strokes. If you check the &lt;a href=&quot;http://www.heartandstroke.com/site/c.ikIQLcMWJtE/b.8747093/k.32AA/Our_Impact.htm&quot; target=&quot;_blank&quot;&gt;&lt;b&gt;impact page&lt;/b&gt;&lt;/a&gt; you will read that they prevent deaths of more than 69,000 Canadians a year. If you think that the number is quite a big, It would be bigger if not for the prevention program that they run. Another thing I found out was that they contributed to the first ever heart transplant in Canada and were instrumental in many prevention programs to reduce heart related diseases by 75% in Canada. They even tell you where the money goes that you donate on the &lt;a href=&quot;http://www.heartandstroke.com/site/c.ikIQLcMWJtE/b.8716195/k.7C89/Your_donation_at_work.htm&quot; target=&quot;_blank&quot;&gt;&lt;b&gt;your donation at work page&lt;/b&gt;&lt;/a&gt; that outlines the details of the programs that they have succeeded. They have some interesting free eTools available for assistance with prevention of heart related diseases at their&amp;nbsp;&lt;a href=&quot;http://www.heartandstroke.com/site/c.ikIQLcMWJtE/b.5374487/k.91C2/Health_eTools.htm&quot; target=&quot;_blank&quot;&gt;&lt;b&gt;etools page&lt;/b&gt;&lt;/a&gt;&lt;b&gt;. &lt;/b&gt;Check it out.&lt;br /&gt;
&lt;br /&gt;
The Ride for heart is great event as there are professionals and&amp;nbsp;amateur&amp;nbsp;cyclists who participate in the race. they have 3 races 25km, 50km and 75km. I will doing the &lt;b&gt;50km &lt;/b&gt;and I am excited to bike on Don Valley Parkway. Those who are not familiar with Don Valley Parkway. It is a main highway in Toronto that connects to the&amp;nbsp;Toronto&amp;nbsp;downtown to rest of GTA and as per law you cannot cycle or walk on it. This is the only time you can&amp;nbsp;cycle on it and I am all for it. I do need to train as 50KM bike ride is tough for me at the moment.&lt;br /&gt;
&lt;br /&gt;
When I was a kid I used to cycle almost everyday in Nairobi, Kenya. It would be my sister, my&amp;nbsp;neighborhood&amp;nbsp;kids and myself we would cycle through valleys, hills, tarmac roads, rough roads and&amp;nbsp;pathways. While doing that without&amp;nbsp;realizing&amp;nbsp;I ended training my body with a great amount stamina and very strong lower body strength. I want to get that back.&lt;br /&gt;
&lt;br /&gt;
Today I work more the 8 hours a day in front of a computer talking to customers over a phone doing IT Technical Support. Without doing the same workout I grew unhealthy and out of shape. I want to get into shape so the easiest way is to take on a&amp;nbsp;regular physical&amp;nbsp;activity that will allow some cardio and some muscular workout. So I started walking every lunch hour and also changed my diet by reducing fatty, salty and carb filled food items and adding more healthy balanced meals. I am a vegetarian but that mean nothing in terms of health if all you eat is chips and drink pop. Now I want to go to next level health with a regular cycling workout so I will train for this ride but also continue it after the event.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;So in all this I need your encouragement and assistance in support Heart and Stroke foundation please donate at my Personal Donation page at &lt;a href=&quot;http://support.heartandstroke.ca/goto/vivekmistry&quot; target=&quot;_blank&quot;&gt;http://support.heartandstroke.ca/goto/vivekmistry&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
If the donations match or exceed $&lt;b&gt;1000&lt;/b&gt;/- I will give a&lt;b&gt;&amp;nbsp;one of a kind&amp;nbsp;handmade art piece&lt;/b&gt; to the top contributor and if there is more than one top contributor then I will run a lottery give the winner the art piece. I will announce the winner on the &lt;b&gt;1st June 2015 &lt;/b&gt;after the event as that is the day when they close the donations as well for the event&lt;b&gt;. This is open to everyone even if you not in&amp;nbsp;Canada&amp;nbsp;I will ship this to you wherever you live.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
You will have to wait for further Updates as I am making this art piece and I will reveal it in the next few weeks. Details to follow.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/2208836122105293651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2015/01/becel-heart-ride-for-heart-2015.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/2208836122105293651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/2208836122105293651'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2015/01/becel-heart-ride-for-heart-2015.html' title='Becel Heart&amp;Stroke Ride for Heart 2015'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-7083732164110061672</id><published>2015-01-14T14:09:00.001-05:00</published><updated>2015-01-16T18:28:02.230-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="photography"/><category scheme="http://www.blogger.com/atom/ns#" term="tips"/><category scheme="http://www.blogger.com/atom/ns#" term="tricks"/><category scheme="http://www.blogger.com/atom/ns#" term="youtube"/><title type='text'>7 Smartphone Photography Tips &amp; Tricks</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
This is a really cool quick hacks/diy tips and tricks that can enhance your photos with your smartphone. Even if you have an old smartphone. check it out.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/fTz4Nhgm_SQ&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/7083732164110061672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2015/01/7-smartphone-photography-tips-tricks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7083732164110061672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7083732164110061672'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2015/01/7-smartphone-photography-tips-tricks.html' title='7 Smartphone Photography Tips &amp; Tricks'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/fTz4Nhgm_SQ/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-1314540617003733011</id><published>2015-01-12T16:22:00.001-05:00</published><updated>2015-01-12T16:29:44.657-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="I am"/><category scheme="http://www.blogger.com/atom/ns#" term="JeSuisCharlie"/><title type='text'>Je suis Charlie</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;Je suis Charlie&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;I am Charlie&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;Je suis Vivek&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;I am Vivek&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;Je suis Humain premier&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;I am Human first&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;#JeSuisCharlie&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/1314540617003733011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2015/01/je-suis-charlie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1314540617003733011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1314540617003733011'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2015/01/je-suis-charlie.html' title='Je suis Charlie'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-4877926097953931584</id><published>2015-01-05T09:51:00.002-05:00</published><updated>2015-01-05T09:51:32.075-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="150 years"/><category scheme="http://www.blogger.com/atom/ns#" term="bank of canada"/><category scheme="http://www.blogger.com/atom/ns#" term="canada"/><category scheme="http://www.blogger.com/atom/ns#" term="Confederation"/><title type='text'>150th anniversary of Confederation of Canada</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In &lt;b&gt;July 1st, 1867&lt;/b&gt; Canada was born with confederation with the &lt;b&gt;4 &lt;/b&gt;provinces &lt;b&gt;Ontario&lt;/b&gt;, &lt;b&gt;Quebec&lt;/b&gt;, &lt;b&gt;Nova Scotia &lt;/b&gt;and &lt;b&gt;New Brunswick&lt;/b&gt; all of which were &lt;b&gt;British Colonies&lt;/b&gt;. Now in &lt;b&gt;2017 &lt;/b&gt;is approaching and &lt;b&gt;Bank of Canada&lt;/b&gt; in celebration of this will be releasing a new note to commemorate that &lt;b&gt;150 &lt;/b&gt;years. However they are not going to just release them they want public feedback. so if you are a Canadian you should put your comments it is a quick survey on what symbols and themes should be on the new note. So below are links for the press release and the survey Check it out and go for it tell that what matters to you as a Canadian.&lt;br /&gt;
&lt;br /&gt;
Check out the links&lt;br /&gt;
Link to press release:&amp;nbsp;&lt;a href=&quot;http://www.bankofcanada.ca/banknotes/new-bank-note-canadas-150th/&quot;&gt;http://www.bankofcanada.ca/banknotes/new-bank-note-canadas-150th/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Link to survey: &lt;a href=&quot;http://www.ipsosresearch.com/c150/&quot;&gt;http://www.ipsosresearch.com/c150/&lt;/a&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/4877926097953931584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2015/01/150th-anniversary-of-confederation-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4877926097953931584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4877926097953931584'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2015/01/150th-anniversary-of-confederation-of.html' title='150th anniversary of Confederation of Canada'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-4546981345235136557</id><published>2014-12-18T17:24:00.002-05:00</published><updated>2014-12-18T17:24:52.384-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="appliance"/><category scheme="http://www.blogger.com/atom/ns#" term="customize"/><category scheme="http://www.blogger.com/atom/ns#" term="devices"/><category scheme="http://www.blogger.com/atom/ns#" term="linkedin"/><category scheme="http://www.blogger.com/atom/ns#" term="pulse"/><title type='text'>first post on linkedin pulse</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
I just posted my first ever post on Linkedin pulse. 5 reasons not to customize appliance devices.&lt;br /&gt;
&lt;br /&gt;
here is the link check it out let me know what you think about it.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.linkedin.com/pulse/5-reasons-customizing-appliance-vivek-mistry&quot;&gt;https://www.linkedin.com/pulse/5-reasons-customizing-appliance-vivek-mistry&lt;/a&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/4546981345235136557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/12/first-post-on-linkedin-pulse.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4546981345235136557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4546981345235136557'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/12/first-post-on-linkedin-pulse.html' title='first post on linkedin pulse'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-7010340876178824166</id><published>2014-11-30T13:23:00.001-05:00</published><updated>2014-11-30T13:23:14.378-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="builders"/><category scheme="http://www.blogger.com/atom/ns#" term="cool"/><category scheme="http://www.blogger.com/atom/ns#" term="custom"/><category scheme="http://www.blogger.com/atom/ns#" term="pc"/><title type='text'>Cool custom pc builders you might not have heard</title><content type='html'>&lt;p dir=&quot;ltr&quot;&gt;Came across a nice list of custom pc builders that I want to try check it.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Five Best Custom PC Builders - http://pulse.me/s/3a2DOt&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/7010340876178824166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/11/cool-custom-pc-builders-you-might-not.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7010340876178824166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7010340876178824166'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/11/cool-custom-pc-builders-you-might-not.html' title='Cool custom pc builders you might not have heard'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-1954292421923780621</id><published>2014-10-17T19:39:00.001-04:00</published><updated>2014-10-17T19:39:29.396-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="song"/><category scheme="http://www.blogger.com/atom/ns#" term="sting"/><category scheme="http://www.blogger.com/atom/ns#" term="talk"/><category scheme="http://www.blogger.com/atom/ns#" term="ted"/><category scheme="http://www.blogger.com/atom/ns#" term="video"/><title type='text'>Sting at Ted</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
I started watching one Ted.com video daily. Today I saw a video of Sting. He was rocking with the music. As an artist he talked how he got a writers block and could not come up with any new songs. Later on when working in shipping yard he started writing about other people and while doing that he found more about him self. Check out the video below or just go the ted talk.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.ted.com/talks/sting_how_i_started_writing_songs_again&quot;&gt;http://www.ted.com/talks/sting_how_i_started_writing_songs_again&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;315&quot; mozallowfullscreen=&quot;&quot; scrolling=&quot;no&quot; src=&quot;https://embed-ssl.ted.com/talks/sting_how_i_started_writing_songs_again.html&quot; webkitallowfullscreen=&quot;&quot; width=&quot;560&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/1954292421923780621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/10/sting-at-ted.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1954292421923780621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/1954292421923780621'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/10/sting-at-ted.html' title='Sting at Ted'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-8247640982705087649</id><published>2014-10-15T22:21:00.001-04:00</published><updated>2014-10-15T22:31:55.674-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="2014"/><category scheme="http://www.blogger.com/atom/ns#" term="bryan lunduke"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="video"/><category scheme="http://www.blogger.com/atom/ns#" term="youtube"/><title type='text'>&quot;Linux Sucks&quot; - 2014</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Found this video recently that talks about what sucks on Linux. Bryan Lunduke states the problems with linux and what really is the problem. I really like how he ended the video stating the main objective of Linux being free is because simply it is used by people for their own use. Does not matter what or how it is used. what matters it is used for what you need it for. the Keyword is &lt;b&gt;YOU.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/5pOxlazS3zs&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/8247640982705087649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/10/linux-sucks-2014.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/8247640982705087649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/8247640982705087649'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/10/linux-sucks-2014.html' title='&quot;Linux Sucks&quot; - 2014'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/5pOxlazS3zs/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-875445021382296270</id><published>2014-10-15T15:57:00.001-04:00</published><updated>2014-10-15T15:57:22.300-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="behance"/><category scheme="http://www.blogger.com/atom/ns#" term="coffee table"/><category scheme="http://www.blogger.com/atom/ns#" term="custom"/><category scheme="http://www.blogger.com/atom/ns#" term="google sketchup"/><category scheme="http://www.blogger.com/atom/ns#" term="stage"/><category scheme="http://www.blogger.com/atom/ns#" term="table"/><category scheme="http://www.blogger.com/atom/ns#" term="under $50"/><title type='text'>Custom White Coffee table</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif; font-size: large;&quot;&gt;I created a custom Coffee table for a friend of mine this summer for a stage presentation he needed. I the table height he needed was more than 18&quot;. when he looked around he only found the them to $70 or more in stores. So I build him one costing less than $50. Check out new work on my @Behance portfolio: &quot;Custom White Coffee Table&quot; &lt;a href=&quot;http://bit.ly/1EZ9Wyh&quot;&gt;http://bit.ly/1EZ9Wyh&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/875445021382296270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/10/custom-white-coffee-table.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/875445021382296270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/875445021382296270'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/10/custom-white-coffee-table.html' title='Custom White Coffee table'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-5051108742056099184</id><published>2014-10-08T21:42:00.001-04:00</published><updated>2014-10-08T21:45:14.839-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="design"/><category scheme="http://www.blogger.com/atom/ns#" term="devices"/><category scheme="http://www.blogger.com/atom/ns#" term="infographic"/><category scheme="http://www.blogger.com/atom/ns#" term="responsive"/><category scheme="http://www.blogger.com/atom/ns#" term="webpage"/><title type='text'>Cool way to web design for all devices</title><content type='html'>&lt;p dir=&quot;ltr&quot;&gt;Checkout the link for cool info graphic on &lt;u&gt;how&lt;/u&gt; design a responsive webpage&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;a href=&quot;http://cdn2.hubspot.net/hub/53/file-1608670488-jpg/what-is-responsive-website-design-ig.jpg?t=1412816727906&quot;&gt;http://cdn2.hubspot.net/hub/53/file-1608670488-jpg/what-is-responsive-website-design-ig.jpg?t=1412816727906&lt;/a&gt;&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/5051108742056099184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/10/cool-way-to-web-design-for-all-devices.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/5051108742056099184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/5051108742056099184'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/10/cool-way-to-web-design-for-all-devices.html' title='Cool way to web design for all devices'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-9140691756331606278</id><published>2014-09-27T11:10:00.000-04:00</published><updated>2014-09-27T11:10:03.328-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="16"/><category scheme="http://www.blogger.com/atom/ns#" term="birthday"/><category scheme="http://www.blogger.com/atom/ns#" term="celebration"/><category scheme="http://www.blogger.com/atom/ns#" term="google"/><title type='text'>Happy birthday Google</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Today I found out is the 16th Birthday of Google. Happy Birthday Google. thank you for being there for all the searches when I needed help in school, when I wanted to prove my friends wrong, when I wanted to know thing with the thing in the thing, when I want to be lucky. have a great one&lt;br /&gt;
&lt;br /&gt;
Happy 16th Birthday to you&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/9140691756331606278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/09/happy-birthday-google.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/9140691756331606278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/9140691756331606278'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/09/happy-birthday-google.html' title='Happy birthday Google'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-3042673804486695806</id><published>2014-09-26T00:55:00.001-04:00</published><updated>2014-09-26T00:55:52.021-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="falls"/><category scheme="http://www.blogger.com/atom/ns#" term="nature"/><category scheme="http://www.blogger.com/atom/ns#" term="niagara"/><category scheme="http://www.blogger.com/atom/ns#" term="Niagara falls"/><category scheme="http://www.blogger.com/atom/ns#" term="water"/><title type='text'>20seconds of awesome niagara falls</title><content type='html'>&lt;p dir=&quot;ltr&quot;&gt;20 seconds of the The great Niagara falls: http://youtu.be/qKvPAxrJ8D0&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/3042673804486695806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/09/20seconds-of-awesome-niagara-falls.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/3042673804486695806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/3042673804486695806'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/09/20seconds-of-awesome-niagara-falls.html' title='20seconds of awesome niagara falls'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-4358254108576132599</id><published>2014-09-24T09:40:00.000-04:00</published><updated>2014-09-24T09:40:33.358-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Blender 3D"/><category scheme="http://www.blogger.com/atom/ns#" term="editing"/><category scheme="http://www.blogger.com/atom/ns#" term="free"/><category scheme="http://www.blogger.com/atom/ns#" term="learning"/><category scheme="http://www.blogger.com/atom/ns#" term="opensource"/><category scheme="http://www.blogger.com/atom/ns#" term="training"/><category scheme="http://www.blogger.com/atom/ns#" term="tutorial"/><category scheme="http://www.blogger.com/atom/ns#" term="video"/><category scheme="http://www.blogger.com/atom/ns#" term="youtube"/><title type='text'>Free Video Editor Tutorial (Blender3D)</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Ever Wonder How to Edit a video for free. You can use plenty of software out there. However I found this video tutorial on blender and I really like it. The playlist seems get updates time to time so you can learn new things on the video editing with Blender 3D. Blender 3d is an opensource Digital Animation and Graphics suite that is available free from &lt;a href=&quot;http://www.blender.org/&quot;&gt;www.blender.org&lt;/a&gt;&amp;nbsp;check it out.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;344&quot; src=&quot;http://www.youtube.com/embed/videoseries?list=PLjyuVPBuorqIhlqZtoIvnAVQ3x18sNev4&quot; width=&quot;425&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/4358254108576132599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/09/free-video-editor-tutorial-blender3d.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4358254108576132599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4358254108576132599'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/09/free-video-editor-tutorial-blender3d.html' title='Free Video Editor Tutorial (Blender3D)'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/videoseries/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-6075470942195426814</id><published>2014-09-23T07:34:00.001-04:00</published><updated>2014-09-23T07:35:56.997-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cheap"/><category scheme="http://www.blogger.com/atom/ns#" term="india"/><category scheme="http://www.blogger.com/atom/ns#" term="mars"/><category scheme="http://www.blogger.com/atom/ns#" term="space"/><category scheme="http://www.blogger.com/atom/ns#" term="youtube"/><title type='text'>ISRO&#39;s Mars Orbiter Mission In A Nutshell</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;//www.youtube.com/embed/x4ZkrtuvZrI&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Check out this video it explains India&#39;s space mission to mars. Which might be the cheapest mars mission so far out of all the nations Overall.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/6075470942195426814/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/09/isros-mars-orbiter-mission-in-nutshell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/6075470942195426814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/6075470942195426814'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/09/isros-mars-orbiter-mission-in-nutshell.html' title='ISRO&#39;s Mars Orbiter Mission In A Nutshell'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-718547989836389002</id><published>2014-09-12T20:09:00.001-04:00</published><updated>2014-09-12T20:09:04.191-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="aerial"/><category scheme="http://www.blogger.com/atom/ns#" term="android"/><category scheme="http://www.blogger.com/atom/ns#" term="flickr"/><category scheme="http://www.blogger.com/atom/ns#" term="galaxy nexus"/><category scheme="http://www.blogger.com/atom/ns#" term="india"/><category scheme="http://www.blogger.com/atom/ns#" term="photos"/><category scheme="http://www.blogger.com/atom/ns#" term="smartphone"/><title type='text'>Aerial photos from Trip to india</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
When to India on the way there and back I took nice photos using my Galaxy nexus an android smartphone. I uploaded them in flickr checkout the images.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;object height=&quot;375&quot; width=&quot;500&quot;&gt; &lt;param name=&quot;flashvars&quot; value=&quot;offsite=true&amp;lang=en-us&amp;page_show_url=%2Fphotos%2Felephantatech%2Fsets%2F72157647474617061%2Fshow%2F&amp;page_show_back_url=%2Fphotos%2Felephantatech%2Fsets%2F72157647474617061%2F&amp;set_id=72157647474617061&amp;jump_to=&quot;&gt;&lt;/param&gt;
&lt;param name=&quot;movie&quot; value=&quot;https://www.flickr.com/apps/slideshow/show.swf?v=1811922554&quot;&gt;&lt;/param&gt;
&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;
&lt;embed type=&quot;application/x-shockwave-flash&quot; src=&quot;https://www.flickr.com/apps/slideshow/show.swf?v=1811922554&quot; allowFullScreen=&quot;true&quot; flashvars=&quot;offsite=true&amp;lang=en-us&amp;page_show_url=%2Fphotos%2Felephantatech%2Fsets%2F72157647474617061%2Fshow%2F&amp;page_show_back_url=%2Fphotos%2Felephantatech%2Fsets%2F72157647474617061%2F&amp;set_id=72157647474617061&amp;jump_to=&quot; width=&quot;500&quot; height=&quot;375&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/718547989836389002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/09/aerial-photos-from-trip-to-india.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/718547989836389002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/718547989836389002'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/09/aerial-photos-from-trip-to-india.html' title='Aerial photos from Trip to india'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-4329791955718122852</id><published>2014-07-12T19:53:00.001-04:00</published><updated>2014-07-12T19:53:39.529-04:00</updated><title type='text'>Toronto downtown Canada Fireworks - July 1st, 2014</title><content type='html'>&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;//www.youtube.com/embed/1l1RLoCKIJo&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/4329791955718122852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/07/toronto-downtown-canada-fireworks-july.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4329791955718122852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/4329791955718122852'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/07/toronto-downtown-canada-fireworks-july.html' title='Toronto downtown Canada Fireworks - July 1st, 2014'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-2503520830223518971</id><published>2014-04-23T20:11:00.001-04:00</published><updated>2014-04-23T20:11:57.471-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mobile creation"/><category scheme="http://www.blogger.com/atom/ns#" term="nexus 7"/><category scheme="http://www.blogger.com/atom/ns#" term="photoshop"/><category scheme="http://www.blogger.com/atom/ns#" term="wallpaper"/><title type='text'>New wallpaper created on the tablet for the tablet</title><content type='html'>&lt;p dir=&quot;ltr&quot;&gt;Checkout my latest wallpaper i created on my nexus 7 as my wallpaper I used Photoshop touch for this.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;a href=&quot;http://flic.kr/p/njaJDt&quot;&gt;http://flic.kr/p/njaJDt&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt; &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIdkQQBcdoGhyphenhyphen1WQ3BQtnQocJRaKcs_zkqAlbnA-pM0yJmGXbm9rxg13ywnxQfkHhMnh2m0O6bLSsahuot6JG2XIKK8hSHnxjOurDd_0TbZgzF1W1Ggm86BDbxo9u6nlp_yqay6g4irgI/s1600/Untitled3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt; &lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIdkQQBcdoGhyphenhyphen1WQ3BQtnQocJRaKcs_zkqAlbnA-pM0yJmGXbm9rxg13ywnxQfkHhMnh2m0O6bLSsahuot6JG2XIKK8hSHnxjOurDd_0TbZgzF1W1Ggm86BDbxo9u6nlp_yqay6g4irgI/s640/Untitled3.jpg&quot;&gt; &lt;/a&gt; &lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/2503520830223518971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/04/new-wallpaper-created-on-tablet-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/2503520830223518971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/2503520830223518971'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/04/new-wallpaper-created-on-tablet-for.html' title='New wallpaper created on the tablet for the tablet'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIdkQQBcdoGhyphenhyphen1WQ3BQtnQocJRaKcs_zkqAlbnA-pM0yJmGXbm9rxg13ywnxQfkHhMnh2m0O6bLSsahuot6JG2XIKK8hSHnxjOurDd_0TbZgzF1W1Ggm86BDbxo9u6nlp_yqay6g4irgI/s72-c/Untitled3.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-7228099453484638431</id><published>2014-04-12T20:40:00.001-04:00</published><updated>2014-04-12T20:47:22.218-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="flickr"/><category scheme="http://www.blogger.com/atom/ns#" term="paintnite"/><category scheme="http://www.blogger.com/atom/ns#" term="the office pub"/><category scheme="http://www.blogger.com/atom/ns#" term="water paint"/><title type='text'>Paintnite fun</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://farm4.staticflickr.com/3777/13804640915_97744226f3_o_d.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;310&quot; src=&quot;https://farm4.staticflickr.com/3777/13804640915_97744226f3_o_d.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;paint nite fun - &lt;a href=&quot;https://flic.kr/p/n2SoNg&quot;&gt;https://flic.kr/p/n2SoNg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
I created this painting today at Paintnite at the office pub in downtown toronto. It was great painting, eating and talking with the people there. I went with my wife sort of our art date. They provide the equipment so you don&#39;t have to worry. It was lively environment and like you would in a art class minus the exam pressure and plus the great food. Definitely recommend it for anyone one who wants dinner and paint instead of the old dinner and movie night.&lt;br /&gt;
&lt;br /&gt;
They have this at various location and check out they even have a 45% off coupon on their website.&amp;nbsp;&lt;a href=&quot;http://www.paintnite.com/i/drink-creatively?gclid=COuQyNCc3L0CFexcMgodGg8AgA&quot;&gt;http://www.paintnite.com/i/drink-creatively?gclid=COuQyNCc3L0CFexcMgodGg8AgA&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/7228099453484638431/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/04/paintnite-fun.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7228099453484638431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7228099453484638431'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/04/paintnite-fun.html' title='Paintnite fun'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-7190009771041709</id><published>2014-01-15T11:49:00.001-05:00</published><updated>2014-01-15T11:49:30.032-05:00</updated><title type='text'>winter nesting</title><content type='html'>&lt;div style=&quot;margin: 0 0 10px 0; padding: 0; font-size: 0.8em; line-height: 1.6em;&quot;&gt;&lt;a href=&quot;http://www.flickr.com/photos/elephantatech/11966890614/&quot; title=&quot;winter nesting&quot;&gt;&lt;img src=&quot;http://farm4.staticflickr.com/3823/11966890614_0223231891.jpg&quot; alt=&quot;winter nesting by elephantatech&quot; /&gt;&lt;/a&gt;&lt;br/&gt;&lt;span style=&quot;margin: 0;&quot;&gt;&lt;a href=&quot;http://www.flickr.com/photos/elephantatech/11966890614/&quot;&gt;winter nesting&lt;/a&gt;, a photo by &lt;a href=&quot;http://www.flickr.com/photos/elephantatech/&quot;&gt;elephantatech&lt;/a&gt; on Flickr.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;While travelling on the evening subway to home I remembered birds nesting so I created this piece on Zenbrush for android on my nexus 7.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/7190009771041709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/01/winter-nesting.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7190009771041709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/7190009771041709'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/01/winter-nesting.html' title='winter nesting'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8724348233587699474.post-6820806847390660525</id><published>2014-01-15T11:42:00.001-05:00</published><updated>2014-01-15T11:42:54.753-05:00</updated><title type='text'>ninja battle</title><content type='html'>&lt;div style=&quot;margin: 0 0 10px 0; padding: 0; font-size: 0.8em; line-height: 1.6em;&quot;&gt;&lt;a href=&quot;http://www.flickr.com/photos/elephantatech/11966298595/&quot; title=&quot;ninja battle&quot;&gt;&lt;img src=&quot;http://farm4.staticflickr.com/3681/11966298595_263750bb1c.jpg&quot; alt=&quot;ninja battle by elephantatech&quot; /&gt;&lt;/a&gt;&lt;br/&gt;&lt;span style=&quot;margin: 0;&quot;&gt;&lt;a href=&quot;http://www.flickr.com/photos/elephantatech/11966298595/&quot;&gt;ninja battle&lt;/a&gt;, a photo by &lt;a href=&quot;http://www.flickr.com/photos/elephantatech/&quot;&gt;elephantatech&lt;/a&gt; on Flickr.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;Stick figure ninja battle on a chalk board. created on Zen brush for Android while on the subway.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://techmystery.blogspot.com/feeds/6820806847390660525/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://techmystery.blogspot.com/2014/01/ninja-battle.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/6820806847390660525'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8724348233587699474/posts/default/6820806847390660525'/><link rel='alternate' type='text/html' href='http://techmystery.blogspot.com/2014/01/ninja-battle.html' title='ninja battle'/><author><name>elephantatech</name><uri>http://www.blogger.com/profile/15055313948935517542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>