Jekyll2023-09-05T10:48:14+10:00http://blog.lucas.net.au/feed.xmlJimmy’s BlogDocumenting ITJames LucasUpgrading Postgres when running in Docker2023-06-15T00:00:00+10:002023-06-15T00:00:00+10:00http://blog.lucas.net.au/software/postgres-upgrade-in-docker<p>Major releases of Postgres require upgrading with migration of system tables to new formats. There are multiple ways to upgrade Postgres including pg_upgrade, logical replication, and dump/restore. In this tutorial you will learn how to perform the dump/restore method.</p>
<h1 id="preparing-for-migration">Preparing for migration</h1>
<h2 id="password-hashing">Password Hashing</h2>
<p>Newer hub.docker images use SCRAM-SHA-256 by default in <em>pg_hba.conf</em>, this configuration will prevent Roles using md5 hashing from authenticating.</p>
<p>If upgrading from Postgres <= 10 or if the environment is still using the old MD5 passport hashing, you should migrate to using SCRAM-SHA-256. Alternatively set your configuration back to <em>md5</em> however this is not recommended and has security implications.</p>
<p>To get a list of Roles with md5 hashed passwords that need to be reset you can execute the following SQL query:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">rolname</span> <span class="k">FROM</span> <span class="n">pg_authid</span>
<span class="k">WHERE</span> <span class="n">rolpassword</span> <span class="k">LIKE</span> <span class="s1">'md5%'</span>
<span class="k">AND</span> <span class="n">rolcanlogin</span><span class="p">;</span>
</code></pre></div></div>
<p>or</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">rolname</span> <span class="k">FROM</span> <span class="n">pg_authid</span>
<span class="k">WHERE</span> <span class="n">rolpassword</span> <span class="k">NOT</span> <span class="k">LIKE</span> <span class="s1">'SCRAM-SHA-256%'</span>
<span class="k">AND</span> <span class="n">rolcanlogin</span><span class="p">;</span>
</code></pre></div></div>
<p>Passwords can then be reset with <strong><em>psql</em></strong> using</p>
<pre><code class="language-postgresql">\password username
</code></pre>
<p>for each Role.</p>
<p>There are many online tutorials for this process: https://www.cybertec-postgresql.com/en/from-md5-to-scram-sha-256-in-postgresql/</p>
<h2 id="volume-mapping">Volume mapping</h2>
<p>The dump/restore method requires that additional disk space equal to the current database size plus a little bit more. A new Docker volume or local disk path should be mapped to the new container. <strong>Do not</strong> map the old data directory to the new container.</p>
<h1 id="migration">Migration</h1>
<p>For all commands below, <em>${old_pg_container}</em> will refer to our source container we wish to upgrade, <em>${new_pg_container}</em> will refer to our upgraded container.</p>
<ol>
<li>Stop all applications writing to the current database. Leave the <em>${old_pg_container}</em> container running.</li>
<li>Start the <em>${new_pg_container}</em> with the upgraded version with docker run or by creating a new container via docker-composer, mounting a new persistent data volume</li>
<li>Install any extensions into the new container, but do not install the schema with <code class="language-plaintext highlighter-rouge">CREATE EXTENSION</code></li>
<li>Migrate the SCHEMA and data from <em>${old_pg_container}</em> to <em>${new_pg_container}</em>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> docker <span class="nb">exec</span> <span class="nt">-i</span> <span class="k">${</span><span class="nv">old_pg_container</span><span class="k">}</span> su <span class="nt">-c</span> <span class="s2">"pg_dumpall --clean"</span> postgres <span class="se">\</span>
| docker <span class="nb">exec</span> <span class="nt">-i</span> <span class="k">${</span><span class="nv">new_pg_container</span><span class="k">}</span> su <span class="nt">-c</span> <span class="s2">"psql"</span> postgres
</code></pre></div> </div>
</li>
<li>Perform a Vacuum to ensure statistics are gathered
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> docker <span class="nb">exec</span> <span class="nt">-i</span> <span class="k">${</span><span class="nv">new_pg_container</span><span class="k">}</span> su <span class="nt">-c</span> <span class="s2">"vacuumdb -a -z"</span> postgres
</code></pre></div> </div>
</li>
<li>Stop <em>${old_pg_container}</em></li>
<li>(Optional) Rename <em>${new_pg_container}</em> to <em>${old_pg_container}</em> if you do not wish to modify application configuration
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> docker rename <span class="k">${</span><span class="nv">new_pg_container</span><span class="k">}</span> <span class="k">${</span><span class="nv">old_pg_container</span><span class="k">}</span>
</code></pre></div> </div>
<p>or if running via docker-compose, update the configuration of the old container to point to the new docker image and new data volume.</p>
</li>
</ol>
<p>You should now be able ot start your application(s).</p>James LucasMajor releases of Postgres require upgrading with migration of system tables to new formats. There are multiple ways to upgrade Postgres including pg_upgrade, logical replication, and dump/restore. In this tutorial you will learn how to perform the dump/restore method.Web based Bingo Game to play with friends and family2020-05-12T20:00:00+10:002020-05-12T20:00:00+10:00http://blog.lucas.net.au/software/online-bingo-game<h1 id="bingo">Bingo</h1>
<p>During the 2020 lockdown, we all started looking to video calls, Zoom etc to keep in touch with Friends and Family.
BINGO was one of these games that we had fond memories of playing in the University bar.
I created this online BINGO system because I couldn’t find anything around that was free and simple to start a game without any accounts etc.</p>
<p>You do not need anything special to play this other than an internet connection and device, it works on phones, tablets and desktops.
One player needs to be the Bingo caller, all other participants are players. Everyone opens their respective links and the game begins.</p>
<h3 id="hint">Hint</h3>
<ul>
<li>For some numbers the Bingo caller will be presented with a short phase that goes along with the number, call it out before the number.</li>
<li>Use screen share or camera to allow players to show their completed cards in a video call</li>
</ul>
<h2 id="players-card">Players Card</h2>
<p><a href="https://blog.lucas.net.au/bingo/bingo_card.html">https://blog.lucas.net.au/bingo/bingo_card.html</a></p>
<p>Players start by opening up the players card. A new card is generated automatically. The card will be regenerated on page reload or by clicking on the “New Game” button.</p>
<p>The Bingo caller will call numbers randomly picked. If the number appears on your card, click on that number in your card to mark the number. If you accidentally click the wrong number, clicking on a marked square will remove the mark.</p>
<p>Once you have completed a horizontal, vertical, 5 square diagonal, or the whole card, YELL BINGO as loud as possible. Other players can make note that a line is gone by selecting the completed line via the buttons below the card.</p>
<h2 id="bingo-caller">Bingo Caller</h2>
<p><a href="https://blog.lucas.net.au/bingo/bingo_host.html">https://blog.lucas.net.au/bingo/bingo_host.html</a></p>
<p>On page load, or after clicking the “New Game” button, the system will reset. A random number between 1 and 75 is drawn on each click of the “Draw Number” button.</p>
<h2 id="license">License</h2>
<p>This software is licensed under the Opensource MIT License. Enjoy.</p>James LucasBingoControlling the AdvantageAir MyPlace controller from OpenHab2017-09-28T07:00:00+10:002017-09-28T07:00:00+10:00http://blog.lucas.net.au/smarthome/openhab-myplace-setup<p><img src="http://blog.lucas.net.au/assets/images/openhab-myplace-setup/openhab.png" alt="Openhab Sitemap View" /></p>
<h2 id="pre-requisites">Pre-Requisites</h2>
<ul>
<li>MyPlace Controller</li>
<li>Openhab Installed (OpenHabian is a great ready to go distribution) - Tested with version 2.1+</li>
<li>HTTP Binding (Version 1 Binding http://docs.openhab.org/addons/bindings/http1/readme.html)</li>
</ul>
<p>The setup is simplified if a static IP address is assigned to your MyPlace system by your router rather than a dynamic IP.</p>
<h2 id="steps">Steps</h2>
<h3 id="binding-configuration">Binding configuration</h3>
<p>By default, the HTTP Binding provides support to include the current state or command, or the current date/time. It does this with the help of java.util.Formatter. The AdvantageAir API unfortunately chose to use the HTTP GET method for changing state of the system and has JSON strings in the query paramater. These conflict with java.util.Formatter so we need to disable this feature.</p>
<p>Append to $OPENHAB_CONF/services/http.cfg</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Disable </span>
format <span class="o">=</span> <span class="nb">false</span>
</code></pre></div></div>
<p>It is hoped that in a future release of the API that state changes are changed to HTTP POST.</p>
<h3 id="configure-http-cache-item">Configure HTTP Cache Item</h3>
<p>The Advantage Air API has a single endpoint to query all system state. It is best to cache this query every 2 seconds (2000 milliseconds) to reduce the number of requests to the controller.</p>
<p>Append to $OPENHAB_CONF/services/http.cfg</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>myAir5SystemDataJsonCache.url<span class="o">=</span>http://<MYAIRIP/Hostname>:2025/getSystemData
myAir5SystemDataJsonCache.updateInterval<span class="o">=</span>2000
</code></pre></div></div>
<h3 id="first-item---air-conditioner-running-state">First Item - Air Conditioner Running State</h3>
<p>We start with a our first, and most complicated, switch to show and control the power state for the Air Conditioner.
getSystemData returns a value of “on” or “off” for the AirCon running state. OpenHab expects capatalised ON and OFF for states, we therefore use a simple Javascript transformation to convert these values to switch state.</p>
<p>The field we are looking at looks like this in the getSystemData output (Extra information removed)</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="dl">"</span><span class="s2">aircons</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">ac1</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">info</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">state</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">on</span><span class="dl">"</span> <span class="o">-</span> <span class="nx">Aircon</span> <span class="nx">unit</span> <span class="nx">state</span> <span class="o">-</span> <span class="nx">whether</span> <span class="nx">the</span> <span class="nx">unit</span> <span class="nx">is</span> <span class="dl">"</span><span class="s2">on</span><span class="dl">"</span> <span class="nx">or</span> <span class="dl">"</span><span class="s2">off</span><span class="dl">"</span><span class="p">.</span>
<span class="p">...</span>
</code></pre></div></div>
<p>Create file $OPENHAB_CONF/transformations/myairAC1state.js</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">input</span><span class="p">)[</span><span class="dl">"</span><span class="s2">aircons</span><span class="dl">"</span><span class="p">][</span><span class="dl">"</span><span class="s2">ac1</span><span class="dl">"</span><span class="p">][</span><span class="dl">"</span><span class="s2">info</span><span class="dl">"</span><span class="p">].</span><span class="nx">state</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">()</span>
</code></pre></div></div>
<p>Then create an switch item for this state in $OPENHAB_CONF/items/myair.items configuring it to load the state from the myAir5SystemDataJsonCache http cache and transform it using the transformation we created.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Switch MyAir5_AC1_State <span class="s2">"Ducted Air Conditioner Power"</span> <climate> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span>
<span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:3000:JS(myairAC1state.js)]
}
</span></code></pre></div></div>
<h3 id="aditional-air-conditioner-information-items">Aditional Air Conditioner Information Items</h3>
<p>We can now add the other bits of information state provided by the MyPlace controller</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">aircons</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">ac1</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">info</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">countDownToOff</span><span class="dl">"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span> <span class="nb">Number</span> <span class="k">of</span> <span class="nx">minutes</span> <span class="nx">before</span> <span class="nx">the</span> <span class="nx">aircon</span> <span class="nx">unit</span> <span class="nx">switches</span> <span class="nx">off</span> <span class="p">(</span><span class="mi">0</span> <span class="o">-</span> <span class="nx">disabled</span><span class="p">)</span>
<span class="dl">"</span><span class="s2">countDownToOn</span><span class="dl">"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span> <span class="nb">Number</span> <span class="k">of</span> <span class="nx">minutes</span> <span class="nx">before</span> <span class="nx">the</span> <span class="nx">aircon</span> <span class="nx">unit</span> <span class="nx">switches</span> <span class="nx">on</span> <span class="p">(</span><span class="mi">0</span> <span class="o">-</span> <span class="nx">disabled</span><span class="p">)</span>
<span class="dl">"</span><span class="s2">fan</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">high</span><span class="dl">"</span><span class="p">,</span> <span class="o">-</span> <span class="nx">Fan</span> <span class="nx">speed</span> <span class="o">-</span> <span class="nx">can</span> <span class="nx">be</span> <span class="dl">"</span><span class="s2">low</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">medium</span><span class="dl">"</span> <span class="nx">or</span> <span class="dl">"</span><span class="s2">high</span><span class="dl">"</span><span class="p">.</span> <span class="nx">Note</span> <span class="nx">some</span> <span class="nx">aircon</span> <span class="nx">units</span> <span class="nx">also</span> <span class="nx">support</span> <span class="dl">"</span><span class="s2">auto</span><span class="dl">"</span><span class="p">.</span>
<span class="dl">"</span><span class="s2">mode</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">heat</span><span class="dl">"</span><span class="p">,</span> <span class="o">-</span> <span class="nx">Mode</span> <span class="o">-</span> <span class="nx">can</span> <span class="nx">be</span> <span class="dl">"</span><span class="s2">heat</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">cool</span><span class="dl">"</span> <span class="nx">or</span> <span class="dl">"</span><span class="s2">fan</span><span class="dl">"</span><span class="p">.</span> <span class="nx">Note</span> <span class="nx">some</span> <span class="nx">aircon</span> <span class="nx">units</span> <span class="nx">support</span> <span class="dl">"</span><span class="s2">dry</span><span class="dl">"</span><span class="p">.</span>
<span class="dl">"</span><span class="s2">myZone</span><span class="dl">"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span> <span class="nx">MyZone</span> <span class="nx">settings</span> <span class="o">-</span> <span class="nx">can</span> <span class="nx">be</span> <span class="kd">set</span> <span class="nx">to</span> <span class="nx">any</span> <span class="nx">zone</span> <span class="nx">that</span> <span class="nx">has</span> <span class="nx">a</span> <span class="nx">temperature</span> <span class="nx">sensor</span> <span class="p">(</span><span class="mi">0</span> <span class="o">-</span> <span class="nx">disabled</span><span class="p">)</span>
<span class="dl">"</span><span class="s2">setTemp</span><span class="dl">"</span><span class="p">:</span> <span class="mf">24.0</span><span class="p">,</span> <span class="o">-</span> <span class="nb">Set</span> <span class="nx">temperature</span> <span class="k">of</span> <span class="nx">the</span> <span class="nx">aircon</span> <span class="nx">unit</span> <span class="o">-</span> <span class="k">this</span> <span class="nx">will</span> <span class="nx">show</span> <span class="nx">the</span> <span class="nx">MyZone</span> <span class="kd">set</span> <span class="nx">temperature</span> <span class="k">if</span> <span class="nx">a</span> <span class="nx">MyZone</span> <span class="nx">is</span> <span class="kd">set</span><span class="p">.</span>
</code></pre></div></div>
<p>These values are either Strings or Numbers. We can extract their values using JSONPATH instead of creating a JavaScript transform.</p>
<p>Append to the file $OPENHAB_CONF/items/myair.items which we created for the status switch.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>String MyAir5_AC1_Mode <span class="s2">"Mode"</span> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.info.mode)]"</span> <span class="o">}</span>
String MyAir5_AC1_FanSpeed <span class="s2">"Fan Speed"</span> <fan> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.info.fan)]"</span> <span class="o">}</span>
Number MyAir5_AC1_TimerOff <span class="s2">"Timer to Off"</span> <clock> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.info.countDownToOff)]"</span> <span class="o">}</span>
Number MyAir5_AC1_TimerOn <span class="s2">"Timer to On"</span> <clock-on> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.info.countDownToOn)]"</span> <span class="o">}</span>
Number MyAir5_AC1_MyZone <span class="s2">"MyZone"</span> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.info.myZone)]"</span> <span class="o">}</span>
Number MyAir5_AC1_SetTemp <span class="s2">"MyZone"</span> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.info.setTemp)]"</span> <span class="o">}</span>
</code></pre></div></div>
<h3 id="zone-configuration">Zone Configuration</h3>
<p>Zones can be configured in multiple ways. In this example we will look at two values temperature and setpoint. This assumes a MyZone setup. If you are using Return Air temperature then the setTemp value from the previous step will need to be used.</p>
<p>Output for Zone 1 in getSystemData:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">zones</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">z01</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">FREEGGVFUX</span><span class="dl">"</span><span class="p">,</span> <span class="o">-</span> <span class="nx">Name</span> <span class="k">of</span> <span class="nx">zone</span> <span class="o">-</span> <span class="nx">max</span> <span class="mi">12</span> <span class="nx">displayed</span> <span class="nx">characters</span>
<span class="dl">"</span><span class="s2">setTemp</span><span class="dl">"</span><span class="p">:</span> <span class="mf">25.0</span><span class="p">,</span> <span class="o">-</span> <span class="nb">Set</span> <span class="nx">temperature</span> <span class="k">of</span> <span class="nx">the</span> <span class="nx">zone</span> <span class="o">-</span> <span class="nx">only</span> <span class="nx">valid</span> <span class="nx">when</span> <span class="nx">Zone</span> <span class="nx">type</span> <span class="o">></span> <span class="mi">0</span><span class="p">.</span>
<span class="dl">"</span><span class="s2">state</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">open</span><span class="dl">"</span><span class="p">,</span> <span class="o">-</span> <span class="nx">State</span> <span class="k">of</span> <span class="nx">the</span> <span class="nx">zone</span> <span class="o">-</span> <span class="nx">can</span> <span class="nx">be</span> <span class="dl">"</span><span class="s2">open</span><span class="dl">"</span> <span class="nx">or</span> <span class="dl">"</span><span class="s2">close</span><span class="dl">"</span><span class="p">.</span> <span class="nx">Note</span><span class="p">:</span> <span class="nx">that</span> <span class="nx">the</span>
<span class="dl">"</span><span class="s2">type</span><span class="dl">"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span> <span class="nx">Readonly</span> <span class="o">-</span> <span class="nx">Zone</span> <span class="nx">type</span> <span class="o">-</span> <span class="mi">0</span> <span class="nx">means</span> <span class="nx">percentage</span> <span class="p">(</span><span class="nx">use</span> <span class="nx">value</span> <span class="nx">to</span> <span class="nx">change</span><span class="p">),</span> <span class="nx">any</span> <span class="nx">other</span> <span class="nx">number</span> <span class="nx">means</span> <span class="nx">it</span><span class="dl">'</span><span class="s1">s temperature control, use setTemp.
"value": 20 - Percentage value of zone - only valid when Zone type = 0.
}
}
</span></code></pre></div></div>
<p>Append to the file $OPENHAB_CONF/items/myair.items for each zone installed. Changeing z01 to the correct zone number.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Number MyAir5_AC1_Zone1_RoomTemp <span class="s2">"Zone 1 Temperature [%.1f °C]"</span> <temperature> <span class="o">(</span>gMyAir, gTemperatureSensor<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.zones.z01.measuredTemp)]"</span> <span class="o">}</span>
Number MyAir5_AC1_Zone1_SetTemp <span class="s2">"Zone 1 SetPoint [%.1f °C]"</span> <temperature> <span class="o">(</span>gMyAir, gACSetPoint<span class="o">)</span> <span class="o">{</span> <span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JSONPATH(</span><span class="nv">$.</span><span class="s2">aircons.ac1.zones.z01.setTemp)]"</span> <span class="o">}</span>
</code></pre></div></div>
<h3 id="controling-the-system-with-rules">Controling the system with Rules</h3>
<p>Rules allow us to link widgets from the BasicUI to the MyPlace API. Rules are a simpler method available for our use rather than wiring up the switches directly with the HTTP bindings.</p>
<p>For example here is what the Air Conditioning ON/OFF switch (MyAir5_AC1_State) looks like when configured via the bindings. Due to the JSON string passed to setAircon we need to URL encode the parameter.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Switch MyAir5_AC1_State <span class="s2">"Ducted Heating Power"</span> <climate> <span class="o">(</span>gMyAir<span class="o">)</span> <span class="o">{</span>
<span class="nv">http</span><span class="o">=</span><span class="s2">"<[myAir5SystemDataJsonCache:2000:JS(myairAC1state.js)]
>[ON:GET:http://myplaceip:2025/setAircon?json=%7B%22ac1%22%3A%7B%22info%22%3A%7B%22state%22%3A%22on%22%7D%7D%7D]
>[OFF:GET:http://myplaceip:2025/setAircon?json=%7B%22ac1%22%3A%7B%22info%22%3A%7B%22state%22%3A%22off%22%7D%7D%7D]"</span>
<span class="o">}</span>
</code></pre></div></div>
<p>So the simple command string:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">json</span><span class="o">={</span><span class="s2">"ac1"</span>:<span class="o">{</span><span class="s2">"info"</span>:<span class="o">{</span><span class="s2">"state"</span>:<span class="s2">"on"</span><span class="o">}}}</span>
</code></pre></div></div>
<p>becomes the url encoded mess:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">json</span><span class="o">=</span>%7B%22ac1%22%3A%7B%22info%22%3A%7B%22state%22%3A%22on%22%7D%7D%7D
</code></pre></div></div>
<p>Let’s create a new file $OPENHAB_CONF/rules/myair.rules for out rules. We create a variable myAirEndpoint to allow a single point in our rules to be updated if the MyPlace controller IP changes.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">#</span> <span class="nc">MyPlace</span> <span class="nc">SetAircon</span> <span class="no">API</span> <span class="nc">Endpoint</span>
<span class="kt">var</span> <span class="nc">String</span> <span class="n">myAirEndpoint</span> <span class="o">=</span> <span class="s">"http://<MyPlaceIP>:2025/setAircon?json="</span>
</code></pre></div></div>
<p>Rules for each component in the MyPlace system is then defined by reacting to receiving a command on one of our items and trnslating that into a call to setAircon. We use command rather than item value change since the MyPlace system values can be modified by the MyPlace UI or via the phone application.</p>
<p>Example rule for changing the Set Temperature for Zone 1</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rule</span> <span class="s">"SetZone1MyAirSetPoint"</span>
<span class="n">when</span>
<span class="nc">Item</span> <span class="n">MyAir5_AC1_Zone1_SetTemp</span> <span class="n">received</span> <span class="n">command</span>
<span class="n">then</span>
<span class="kt">var</span> <span class="nc">String</span> <span class="n">json</span> <span class="o">=</span> <span class="err">'</span><span class="o">{</span><span class="s">"ac1"</span><span class="o">:{</span><span class="s">"zones"</span><span class="o">:{</span><span class="s">"z01"</span><span class="o">:{</span><span class="s">"setTemp"</span><span class="o">:</span><span class="s">"' + MyAir5_AC1_Zone1_SetTemp.state.toString + '"</span><span class="o">}}}}</span><span class="err">'</span>
<span class="n">sendHttpGetRequest</span><span class="o">(</span><span class="n">myAirEndpoint</span> <span class="o">+</span> <span class="n">json</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"UTF-8"</span><span class="o">))</span>
<span class="n">end</span>
</code></pre></div></div>
<p>Set the timer to turn off the AC after a set value</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rule</span> <span class="s">"SetMyAirTimerToOff"</span>
<span class="n">when</span>
<span class="nc">Item</span> <span class="n">MyAir5_AC1_TimerOff</span> <span class="n">received</span> <span class="n">command</span>
<span class="n">then</span>
<span class="kt">var</span> <span class="nc">String</span> <span class="n">json</span> <span class="o">=</span> <span class="err">'</span><span class="o">{</span><span class="s">"ac1"</span><span class="o">:{</span><span class="s">"info"</span><span class="o">:{</span><span class="s">"countDownToOff"</span><span class="o">:</span><span class="err">'</span> <span class="o">+</span> <span class="n">MyAir5_AC1_TimerOff</span><span class="o">.</span><span class="na">state</span><span class="o">.</span><span class="na">toString</span> <span class="o">+</span> <span class="err">'</span><span class="o">}}}</span><span class="err">'</span>
<span class="n">sendHttpGetRequest</span><span class="o">(</span><span class="n">myAirEndpoint</span> <span class="o">+</span> <span class="n">json</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"UTF-8"</span><span class="o">))</span>
<span class="n">end</span>
</code></pre></div></div>
<p>When changing the zone used by MyZone you need to ensure two commands are sent. The first is to ensure the Zone we want to enable is in an open position, changing the MyZone value doesn’t automatically do this. Secondly we need to set the MyZone value.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rule</span> <span class="s">"SetMyAirMyZone"</span>
<span class="n">when</span>
<span class="nc">Item</span> <span class="n">MyAir5_AC1_MyZone</span> <span class="n">received</span> <span class="n">command</span>
<span class="n">then</span>
<span class="n">json</span> <span class="o">=</span> <span class="err">'</span><span class="o">{</span><span class="s">"ac1"</span><span class="o">:{</span><span class="s">"zones"</span><span class="o">:{</span><span class="s">"z0' + MyAir5_AC1_MyZone.state.toString + '"</span><span class="o">:{</span><span class="s">"state"</span><span class="o">:</span><span class="s">"open"</span><span class="o">}}}}</span><span class="err">'</span>
<span class="n">sendHttpGetRequest</span><span class="o">(</span><span class="n">myAirEndpoint</span> <span class="o">+</span> <span class="n">json</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"UTF-8"</span><span class="o">))</span>
<span class="kt">var</span> <span class="nc">String</span> <span class="n">json</span> <span class="o">=</span> <span class="err">'</span><span class="o">{</span><span class="s">"ac1"</span><span class="o">:{</span><span class="s">"info"</span><span class="o">:{</span><span class="s">"myZone"</span><span class="o">:</span><span class="err">'</span> <span class="o">+</span> <span class="n">MyAir5_AC1_MyZone</span><span class="o">.</span><span class="na">state</span><span class="o">.</span><span class="na">toString</span> <span class="o">+</span> <span class="err">'</span><span class="o">}}}</span><span class="err">'</span>
<span class="n">sendHttpGetRequest</span><span class="o">(</span><span class="n">myAirEndpoint</span> <span class="o">+</span> <span class="n">json</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"UTF-8"</span><span class="o">))</span>
<span class="n">end</span>
</code></pre></div></div>
<p>Note: the example above will not work for a 10th zone if you have all 10 zones installed. The json for opening the zone needs to be adjusted for this to work.</p>
<h3 id="example-sitemap-for-basicui">Example Sitemap for BasicUI</h3>
<p>$OPENHAB_CONF/sitemap/default.sitemap (Or choose your sitemap)</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Frame <span class="nv">label</span><span class="o">=</span><span class="s2">"Climate"</span> <span class="o">{</span>
Switch <span class="nv">item</span><span class="o">=</span>MyAir5_AC1_State
Selection <span class="nv">item</span><span class="o">=</span>MyAir5_AC1_Mode <span class="nv">label</span><span class="o">=</span><span class="s2">"AC Mode"</span> <span class="nv">mappings</span><span class="o">=[</span><span class="nv">cool</span><span class="o">=</span><span class="s2">"Cool"</span>, <span class="nv">heat</span><span class="o">=</span><span class="s2">"Heat"</span>, <span class="nv">vent</span><span class="o">=</span><span class="s2">"Fan"</span>, <span class="nv">dry</span><span class="o">=</span><span class="s2">"DRY"</span><span class="o">]</span>
Selection <span class="nv">item</span><span class="o">=</span>MyAir5_AC1_MyZone <span class="nv">label</span><span class="o">=</span><span class="s2">"MyZone"</span> <span class="nv">mappings</span><span class="o">=[</span><span class="nv">1</span><span class="o">=</span><span class="s2">"Room 1"</span>, <span class="nv">2</span><span class="o">=</span><span class="s2">"Room 2"</span>, <span class="nv">3</span><span class="o">=</span><span class="s2">"Room 3"</span><span class="o">]</span>
Text <span class="nv">item</span><span class="o">=</span>MyAir5_AC1_Zone1_RoomTemp
Setpoint <span class="nv">item</span><span class="o">=</span>MyAir5_AC1_Zone1_SetTemp
<span class="o">}</span>
</code></pre></div></div>
<h3 id="control-away">Control away</h3>
<p>Browse to your BasicUI sitemap you just modified, and you can control the AC Power State, Mode, MyZone and Set Point for Zone 1. You can also see the current temperature recorded in Zone 1.</p>
<p><img src="http://blog.lucas.net.au/assets/images/openhab-myplace-setup/openhab.png" alt="Openhab Sitemap View" /></p>James Lucas