<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>The Scrapheap Architect</title>
    <description>Exploring technology and the institutions that create it</description>
    <link>http://www.leocrawford.org.uk/</link>
    <atom:link href="http://www.leocrawford.org.uk/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 07 Nov 2025 20:21:15 +0000</pubDate>
    <lastBuildDate>Fri, 07 Nov 2025 20:21:15 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Fixing the Moes TS0601 Thermostat on Home Assistant (ZHA)</title>
        <description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;This post documents my journey debugging and fixing a &lt;strong&gt;Moes ZHT-002 / TS0601 thermostat&lt;/strong&gt; (manufacturer ID &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_TZE204_xalsoe3m&lt;/code&gt;) when connected to Home Assistant using &lt;strong&gt;ZHA&lt;/strong&gt;.&lt;br /&gt;
Out of the box, ZHA recognized it as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TS0601&lt;/code&gt; Tuya device, but exposed almost no useful controls — just a generic “Router” entity and a few disabled diagnostics.&lt;/p&gt;

&lt;p&gt;It became clear that I needed a &lt;strong&gt;custom ZHA quirk&lt;/strong&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;ZHA loaded the device using the generic Tuya TS0601 handler, but no climate controls worked:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No temperature control&lt;/strong&gt; or feedback&lt;/li&gt;
  &lt;li&gt;“Unknown” HVAC mode and “Firmware: unknown”&lt;/li&gt;
  &lt;li&gt;Logs showed it registering as a plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zigpy.device.Device&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;No quirk was being matched, even though one existed for other TS0601 models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Home Assistant logs were silent about quirk application:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Device: _TZE204_xalsoe3m TS0601  
Quirk: zigpy.device.Device
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It was time to make my own.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-journey&quot;&gt;The Journey&lt;/h2&gt;

&lt;h3 id=&quot;step-1-discovering-the-device-signature&quot;&gt;Step 1: Discovering the Device Signature&lt;/h3&gt;

&lt;p&gt;Using ZHA’s device info dump:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;manufacturer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_TZE204_xalsoe3m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;TS0601&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;endpoints&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;profile_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0104&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;device_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0051&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;input_clusters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0004&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0005&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0xef00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;output_clusters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x000a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0019&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This confirmed it was a Tuya MCU device using the &lt;strong&gt;EF00&lt;/strong&gt; cluster.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;step-2-creating-a-custom-quirk&quot;&gt;Step 2: Creating a Custom Quirk&lt;/h3&gt;

&lt;p&gt;Custom quirks live under:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/config/custom_zha_quirks/tuya/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I started with a minimal working version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tuya_thermostat.py&lt;/code&gt; that matched the fingerprint and confirmed it loaded:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Loaded custom quirks. Please contribute them to https://github.com/zigpy/zha-device-handlers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That first milestone meant: ✅ my code was being loaded.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;debugging-the-datapoints-dps&quot;&gt;Debugging the Datapoints (DPs)&lt;/h2&gt;

&lt;p&gt;Tuya thermostats expose data via &lt;em&gt;datapoints (DPs)&lt;/em&gt; rather than standard attributes.&lt;/p&gt;

&lt;p&gt;To debug, I instrumented the MCU cluster:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_dp_2_attr_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dp_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tuya.tuya_thermostat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dp_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;dp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tuya_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dp_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;raw_bytes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;decoded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;as_value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;raw_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[DP→Attr] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manufacturer&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; dp=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; decoded=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decoded&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This produced live logs of all Tuya datapoints:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[DP→Attr] _TZE204_xalsoe3m dp=18 decoded=2100  
[DP→Attr] _TZE204_xalsoe3m dp=50 decoded=2000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From this, I discovered:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;DP&lt;/th&gt;
      &lt;th&gt;Meaning&lt;/th&gt;
      &lt;th&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;16&lt;/td&gt;
      &lt;td&gt;Actual temperature (°C × 10)&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;18&lt;/td&gt;
      &lt;td&gt;Room temperature sensor value&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;40&lt;/td&gt;
      &lt;td&gt;Child lock (bool)&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;Setpoint temperature (°C × 100 scaling confirmed)&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;That meant the scaling was &lt;strong&gt;10× off&lt;/strong&gt; in my first attempt!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-3-fixing-scaling-and-mapping&quot;&gt;Step 3: Fixing Scaling and Mapping&lt;/h2&gt;

&lt;p&gt;After trial and error, and comparing with &lt;strong&gt;Zigbee2MQTT’s Moes ZHT-002 mapping&lt;/strong&gt;, I confirmed:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_temperature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;converter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;occupied_heating_setpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;converter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dp_converter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This finally produced working temperature reporting and adjustable setpoints within Home Assistant.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-4-adding-more-features&quot;&gt;Step 4: Adding More Features&lt;/h2&gt;

&lt;p&gt;Borrowing ideas from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ts0601_trv.py&lt;/code&gt; (the ZHA TRV quirk) and Zigbee2MQTT’s Tuya converter, I added:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Min/max temperature limits&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Temperature calibration offset&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sensor source mode&lt;/strong&gt; (internal, external, or both)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Workday mode selection&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Child lock and frost protection&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are declared using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tuya_switch()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tuya_enum()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tuya_number()&lt;/code&gt; calls in the builder.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-5-the-final-working-quirk&quot;&gt;Step 5: The Final Working Quirk&lt;/h2&gt;

&lt;p&gt;Here’s the simplified working core for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_TZE204_xalsoe3m&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TuyaQuirkBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_TZE204_xalsoe3m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;TS0601&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_temperature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;converter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_dp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;occupied_heating_setpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;converter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dp_converter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;child_lock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;translation_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;child_lock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fallback_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Child lock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;working_day&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;enum_class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkingDayV02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;translation_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;working_day&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fallback_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Working day mode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuya_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dp_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AttributeDefs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_temperature_calibration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int16s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;min_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnitOfTemperature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CELSIUS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;translation_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;local_temperature_calibration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fallback_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Temperature calibration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TuyaThermostat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;skip_configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_to_registry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;step-6-success-&quot;&gt;Step 6: Success 🎉&lt;/h2&gt;

&lt;p&gt;After restarting Home Assistant, ZHA loaded:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Quirk: zhaquirks.tuya.tuya_thermostat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;climate entity&lt;/strong&gt; now exposes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Current and target temperature&lt;/li&gt;
  &lt;li&gt;Mode switching (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heat&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;off&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Child lock toggle&lt;/li&gt;
  &lt;li&gt;Calibration offset&lt;/li&gt;
  &lt;li&gt;Workday mode&lt;/li&gt;
  &lt;li&gt;Correct scaling for both actual and setpoint readings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yes — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[DP→Attr]&lt;/code&gt; logs still print decoded DP updates for debugging!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Tuya devices don’t speak Zigbee directly&lt;/strong&gt; — everything is custom MCU DPs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Start simple&lt;/strong&gt; — confirm your quirk loads, then map one DP at a time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Always decode TuyaData properly&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.as_value&lt;/code&gt; is your friend.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Borrow from Zigbee2MQTT and ZHA TRV quirks&lt;/strong&gt; — they’re excellent references.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keep logging while you experiment&lt;/strong&gt; — it’s the only visibility you get.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;The TS0601 thermostats are not hopeless — they just need a translator.&lt;br /&gt;
With a working custom quirk, ZHA can handle them as cleanly as Zigbee2MQTT, no extra gateway needed.&lt;/p&gt;

&lt;p&gt;If you’re wrestling with a Tuya TS0601 device, start by identifying your &lt;strong&gt;DP map&lt;/strong&gt; — everything else falls into place from there.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Repository:&lt;/em&gt; &lt;a href=&quot;https://github.com/leocrawford/leocrawford.github.io&quot;&gt;leocrawford.github.io&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;Device:&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_TZE204_xalsoe3m&lt;/code&gt; (Moes ZHT-002)&lt;br /&gt;
&lt;em&gt;Home Assistant Core:&lt;/em&gt; 2025.10.4&lt;br /&gt;
&lt;em&gt;Integration:&lt;/em&gt; ZHA (Texas Instruments CC2531 coordinator)&lt;/p&gt;
</description>
        <pubDate>Sat, 01 Nov 2025 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/homeassistant/zigbee/tuya/thermostat/debugging/2025/11/01/fixing-tuya-ts0601-thermostat-on-zha.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/homeassistant/zigbee/tuya/thermostat/debugging/2025/11/01/fixing-tuya-ts0601-thermostat-on-zha.html</guid>
        
        <category>homeassistant</category>
        
        <category>zha</category>
        
        <category>tuya</category>
        
        <category>zigbee</category>
        
        <category>thermostat</category>
        
        <category>moes</category>
        
        
        <category>homeassistant</category>
        
        <category>zigbee</category>
        
        <category>tuya</category>
        
        <category>thermostat</category>
        
        <category>debugging</category>
        
      </item>
    
      <item>
        <title>Connecting Texecom Premier Using Esp8266</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://github.com/leocrawford/leocrawford.github.io/assets/915016/1fd32687-e637-4233-84fb-bf53f34fa555&quot; alt=&quot;PXL_20230621_132420915&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For some time I have been using a Raspberry Pi Zero W to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ser2net&lt;/code&gt; relaying the COM port of my Texecom Premier Elite 48 over wifi
so that I can connect wintex remotely. Latterly I have moved to using home assistant to automate my alarm using &lt;a href=&quot;https://hub.docker.com/r/dchesterton/texecom2mqtt&quot;&gt;this excellent HA add-on&lt;/a&gt;. 
I must say this is awesome; now I can alert my phone if the alarm goes off, and auto arm (and disarm) as I enter/leave the house.&lt;/p&gt;

&lt;p&gt;However I have been dis-satisfied with the raspberry pi solution for a few reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I currently power it with a trailing USB cable which is ugly, and sometimes gets yanked.&lt;/li&gt;
  &lt;li&gt;I’m using the UART pins directly with 5v IO (which they aren’t rated for) and whilst it works I feel uncomfortable about this solution.&lt;/li&gt;
  &lt;li&gt;Patching the pi takes work, and sometimes requires changes to be made&lt;/li&gt;
  &lt;li&gt;The pi seems overkill for the job (and I can use it for other things)&lt;/li&gt;
  &lt;li&gt;Ideally I’d be able to connect two COM ports so I can use wintex and HA at the same time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Inspired by Rogan Dawes in various posts including &lt;a href=&quot;[url](https://community.home-assistant.io/t/integrating-texecom-premier-alarm-panels-via-esphome-using-wintex-protocol/330396)https://community.home-assistant.io/t/integrating-texecom-premier-alarm-panels-via-esphome-using-wintex-protocol/330396&quot;&gt;this&lt;/a&gt;
and &lt;a href=&quot;[url](https://community.home-assistant.io/t/texecom-alarm-panel/40561/54)https://community.home-assistant.io/t/texecom-alarm-panel/40561/54&quot;&gt;this&lt;/a&gt; I decided to dip my toe into 
embedded devices. I bought a few ESP2866 (because they were cheap) and quickly got started with &lt;a href=&quot;[url](https://github.com/jeelabs/esp-link)https://github.com/jeelabs/esp-link&quot;&gt;esp-link&lt;/a&gt; which was
really easy and effective. However the little dev boards I had were still powered over USB, weren’t specified for 5v UART (though they did seem to work) and 
I still only had one COM port connected.&lt;/p&gt;

&lt;p&gt;Via a lot of web searching (and not a little trial and error with aliexpress orders, not all of which I fried) I finally settled on this amazing piece of gear.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/leocrawford/leocrawford.github.io/assets/915016/e3941e86-c25b-4178-912a-a555a75da22d&quot; alt=&quot;esp12e&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It only &lt;a href=&quot;[url](https://www.ebay.co.uk/itm/203202954420)https://www.ebay.co.uk/itm/203202954420&quot;&gt;costs £4.60&lt;/a&gt; but it has an onboard power converter allowing it to be powered over 12V (which the texecom premier provides on the COM port) 
and also has a built in level shifter allowing the UART pins to be 3v or 5v. Now I have a single piece of hardware that can be connected directly into my alarm with no trailing cables, power adapaters or level shifter.&lt;/p&gt;

&lt;p&gt;However I was aware that I still only had one COM port connected (and the ESP8266 can only run one UART using hardware) so what to do? I could have looked at 
ESP32s but then my hunt for the perfect piece of gear would start over (and I must have already spent more with aliexpress than just buying the official Wifi adapter from texecom) so I turned to software. I found some bit banging code and was
contemplating merging it into esp-link (which I realised hadn’t bene updated for three years) when I came across &lt;a href=&quot;[url](https://esphome.io/)https://esphome.io/&quot;&gt;esphome&lt;/a&gt; which has 
built in &lt;a href=&quot;[url](https://esphome.io/components/uart.html)https://esphome.io/components/uart.html&quot;&gt;UART support&lt;/a&gt; and a number of different stream-servers that
relay this serial connection over wifi. I experimented with a few, which either didn’t support ESP2866 or didn’t seem to permit multiple UART connections - but finally found &lt;a href=&quot;[url](https://github.com/2QT-Lexi/esphome-stream-server-v2)&quot;&gt;this one&lt;/a&gt;.
I used HASS to create a new esphome device, and configured it like this..&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;esphome:
  name: alarm-link
  friendly_name: alarm-link

esp8266:
  board: esp12e

external_components:
  - source: github://2QT-Lexi/esphome-stream-server-v2

uart:
 - id: uart_bus
   tx_pin: GPIO1
   rx_pin: GPIO3
   baud_rate: 19200 
   data_bits: 8
   stop_bits: 2
 - id: uart_bus_2
   tx_pin: GPIO4
   rx_pin: GPIO5
   data_bits: 8
   stop_bits: 2
   baud_rate: 19200 

stream_server:
 - uart_id: uart_bus
   port: 6638
   id: &quot;b1&quot;
 - uart_id: uart_bus_2
   port: 6639
   id: &quot;b2&quot;

binary_sensor:
  - platform: stream_server
    stream_server: &quot;b1&quot;
    name: &quot;serial_server_1&quot;
  - platform: stream_server
    stream_server: &quot;b2&quot;
    name: &quot;serial_server_2&quot;
       
# Enable logging
logger:
  baud_rate: 0


# Enable Home Assistant API
api:
  encryption:
    key: &quot;....&quot;

ota:
  password: &quot;....&quot;

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: &quot;Alarm-Link Fallback Hotspot&quot;
    password: &quot;....&quot;

captive_portal:
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It seems that esphome is smart enough to drop back to software (bit banging) for the second UART connection (which I chose to be the one I use infrequently for wintex, not the always connected HASS connection).  Part of my logs now show my device has started up, and using the UART pins I specified has exposed both over two different ports&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[13:42:41][C][uart.arduino_esp8266:103]:   TX Pin: GPIO1
[13:42:41][C][uart.arduino_esp8266:104]:   RX Pin: GPIO3
[13:42:41][C][uart.arduino_esp8266:106]:   RX Buffer Size: 256
[13:42:41][C][uart.arduino_esp8266:108]:   Baud Rate: 19200 baud
[13:42:41][C][uart.arduino_esp8266:109]:   Data Bits: 8
[13:42:41][C][uart.arduino_esp8266:110]:   Parity: NONE
[13:42:41][C][uart.arduino_esp8266:111]:   Stop bits: 2
[13:42:41][C][uart.arduino_esp8266:113]:   Using hardware serial interface.
[13:42:41][C][uart.arduino_esp8266:102]: UART Bus:
[13:42:41][C][uart.arduino_esp8266:103]:   TX Pin: GPIO4
[13:42:41][C][uart.arduino_esp8266:104]:   RX Pin: GPIO5
[13:42:41][C][uart.arduino_esp8266:106]:   RX Buffer Size: 256
[13:42:41][C][uart.arduino_esp8266:108]:   Baud Rate: 19200 baud
[13:42:41][C][uart.arduino_esp8266:109]:   Data Bits: 8
[13:42:41][C][uart.arduino_esp8266:110]:   Parity: NONE
[13:42:41][C][uart.arduino_esp8266:111]:   Stop bits: 2
[13:42:41][C][uart.arduino_esp8266:115]:   Using software serial
[13:42:41][C][captive_portal:088]: Captive Portal:
[13:42:41][C][mdns:108]: mDNS:
[13:42:41][C][mdns:109]:   Hostname: alarm-link
[13:42:41][C][ota:093]: Over-The-Air Updates:
[13:42:41][C][ota:094]:   Address: alarm-link.local:8266
[13:42:41][C][ota:097]:   Using Password.
[13:42:41][C][api:138]: API Server:
[13:42:41][C][api:139]:   Address: alarm-link.local:6053
[13:42:41][C][api:141]:   Using noise encryption: YES
[13:42:41][C][streamserver:110]: Stream Server:
[13:42:41][C][streamserver:111]:   Address: 192.168.1.115:6638
[13:42:41][C][streamserver:110]: Stream Server:
[13:42:41][C][streamserver:111]:   Address: 192.168.1.115:6639
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success. I now have a single cheap device tucked inside the alarm case, with two COM ports wired up, and power drawn from the alarm itself. So far it has been 100% stable and I can manage it remotely (including reflashing) if I need.&lt;/p&gt;
</description>
        <pubDate>Wed, 21 Jun 2023 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/2023/06/21/connecting-texecom-premier-using-esp8266.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/2023/06/21/connecting-texecom-premier-using-esp8266.html</guid>
        
        
      </item>
    
      <item>
        <title>COVID-19: Why testing and tehcnology might be an exit strategy</title>
        <description>&lt;h1 id=&quot;covid-19-why-testing-and-tehcnology-might-be-an-exit-strategy&quot;&gt;COVID-19: Why testing and tehcnology might be an exit strategy&lt;/h1&gt;

&lt;p&gt;The BBC &lt;a href=&quot;https://www.bbc.co.uk/news/health-51963486&quot;&gt;reported&lt;/a&gt; that:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There are essentially three ways out of this mess.&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;vaccination&lt;/li&gt;
    &lt;li&gt;enough people develop immunity through infection&lt;/li&gt;
    &lt;li&gt;or permanently change our behaviour/society&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;But a vaccination is reported to be &lt;a href=&quot;https://www.newscientist.com/article/2237742-how-soon-will-we-have-a-coronavirus-vaccine-the-race-against-covid-19/&quot;&gt;around 12-18 months away&lt;/a&gt; (and could be much longer), and it is  estimated it will take two years for herd immunity to arrive. Both seem like a long way into the future, and based a relatively short (and probably quite soft) experience of social distancing - that seems like a massive burden on individuals, the economy and ultimately society. Is there any way out of this?&lt;/p&gt;

&lt;p&gt;In order to see if there might be more welcome paths through this lets start by testing the constraints on the assumptions above.&lt;/p&gt;

&lt;p&gt;12-18 months for the development of a vaccine would be unprecedented (it usually takes a decade or so, and &lt;a href=&quot;https://www.avert.org/professionals/hiv-science/developing-vaccine&quot;&gt;is expected to take almost 50 years for a HIV vaccine&lt;/a&gt;) but the real problem seems to be the testing:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“We could have a vaccine in three weeks, but we can’t guarantee its safety or efficacy,” says Gary Kobinger, a virologist at Laval University in Canada” &lt;a href=&quot;https://www.newscientist.com/article/2237742-how-soon-will-we-have-a-coronavirus-vaccine-the-race-against-covid-19/#ixzz6HRxR6tLF&quot;&gt;New scientist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, so perhaps we might be able to get a vaccine more quickly if we were less worried about whether it worked and had harmful effects. As a 40-something-year-old without any pre-existing conditions I’m likely to hold out for the gold standard in vaccine testing before I considered it, but I suspect an 85 year old with asthma might feel differently (especially if their quality of life is already greatly diminished by self isolation). Perhaps it would would be possible for those at the very greatest risk to accept more risk with a future vaccine, allowing them earlier access to it.&lt;/p&gt;

&lt;p&gt;Now, let’s look at the herd immunity numbers. There are 66 million people in the UK and I have seen estimates of between &lt;a href=&quot;https://www.nationalgeographic.co.uk/science-and-technology/2020/03/uk-backed-herd-immunity-beat-covid-19-well-ultimately-need-it&quot;&gt;60%&lt;/a&gt; and &lt;a href=&quot;https://www.sciencemediacentre.org/expert-comments-about-herd-immunity/&quot;&gt;80%&lt;/a&gt; required to develop herd immunity. Around &lt;a href=&quot;https://www.statnews.com/2020/03/16/coronavirus-model-shows-hospitals-what-to-expect/&quot;&gt;1% of patents seems to require a ventilator&lt;/a&gt; and they typically require them for a &lt;a href=&quot;https://eu.lohud.com/story/news/health/2020/03/20/ny-may-need-24-000-ventilators-fight-covid-19-how-could-get-them/2877608001/&quot;&gt;few days to a few weeks&lt;/a&gt;. If we assume a week on average and 70% required for herd immunity then we would need 66M * 70% * 1% = 462000 respirator weeks. As the UK has &lt;a href=&quot;https://news.sky.com/story/coronavirus-extraordinary-uk-effort-to-produce-thousands-more-ventilators-11961559&quot;&gt;8175 respirators at the moment&lt;/a&gt; it would take 56 weeks to accommodate that even if every respirator wasn’t needed for anything else, and the government could throttle social distancing sufficiently well to hold on peak demand. Hardly conclusive, but if we assume that the UK could double its number of ventilators and that most of the remainder will be used for non COVID-19 patients then perhaps this validates estimates in the 1-2 year period.&lt;/p&gt;

&lt;p&gt;Obviously the good news with herd immunity is that half way through this period, ~35% of the population will be immune as they have recovered from the virus (assuming it doesn’t mutate and reinfect them). At this stage those individuals could be leading normal lives, safe in the knowledge they won’t become infected or infect others.&lt;/p&gt;

&lt;p&gt;This is where we turn to the practicalities of social distancing and self isolation. At the moment anyone who has the symptoms of COVID-19 (which are not specific to this virus) are expected to stay at home for &lt;a href=&quot;https://www.nhs.uk/conditions/coronavirus-covid-19/self-isolation-advice/&quot;&gt;7 to 14 days&lt;/a&gt;. This  unfortunately means many people who do not have COVID-19 will be self-isolating unnecessarily.&lt;/p&gt;

&lt;p&gt;Conversely many (&lt;a href=&quot;https://www.nature.com/articles/d41586-020-00822-x&quot;&gt;perhaps between 18% and 31%&lt;/a&gt;) people will never show symptoms and as such will not self isolate. More might choose not to because the benefits are for others, whilst the cost (of isolation itself and of lost earnings, etc) is borne by them.&lt;/p&gt;

&lt;p&gt;If only we could reduce the number of people who didn’t need to self-isolate and increase those who did. Fortunately that might be easier than it seems.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A growing number of people will have already been infected and should no longer need to self-isolate. If we could test for those who are immune at scale, then we could lower the burden of unnecessary self-isolation. Fortunately the UK is &lt;a href=&quot;https://www.bbc.co.uk/news/live/world-51994675?ns_mchannel=social&amp;amp;ns_source=twitter&amp;amp;ns_campaign=bbc_live&amp;amp;ns_linkname=5e773b449ea9fc0673e7b020&amp;amp;UK%20%27has%20ordered%20millions%20of%20antibody%20tests%27&amp;amp;2020-03-22T10:50:20.879Z&amp;amp;ns_fee=0&amp;amp;pinned_post_locator=urn:asset:325c5934-ba5f-4872-8ac5-ea8defbfe7f9&amp;amp;pinned_post_asset_id=5e773b449ea9fc0673e7b020&amp;amp;pinned_post_type=share&amp;amp;fbclid=IwAR3RKjwBPW4ET0VkBcrXd-ulIdYyTQhbd7o3_qabAZzIGKJ2_I_91fMXPrU&quot;&gt;already starting to purchase such tests&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;There are emerging &lt;a href=&quot;https://techcrunch.com/2020/03/20/scanwell-aims-to-launch-at-home-15-minute-coronavirus-test-but-it-still-needs-fda-approval/&quot;&gt;15-minute home tests&lt;/a&gt; which could be used to allow people who self-isolate to take themselves more quickly out of isolation if they test negative (perhaps for two days running)&lt;/li&gt;
  &lt;li&gt;Singapore has proved that it is &lt;a href=&quot;https://www.medrxiv.org/content/10.1101/2020.02.14.20023036v1.full.pdf+html&quot;&gt;possible to limit infections&lt;/a&gt; whilst not overly reducing citizens freedoms (e.g. pubs and schools are still open). They have done this through quick and rigorous contact chaining of infected individuals - which is labour intensive and as a &lt;a href=&quot;https://www.medrxiv.org/content/10.1101/2020.02.14.20023036v1.full.pdf+html&quot;&gt;UK research paper&lt;/a&gt; points out “For contact tracing to be an effective public health measure requires secondary cases to be discovered before they become infectious; hence the time from the primary case becoming infectious to the tracing of their contacts needs to be shorter than the incubation period.”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Putting these together we can see that if we were certain about whether people who had symptoms were or were not infected we could either release them from self-isolation more quickly, or we could start looking for the people who they had infected more quickly to ask them to self-isolate too. Of course doing this manually is no longer feasible, but this is something technology can solve, as Singapore have done with their app &lt;a href=&quot;https://www.tracetogether.gov.sg/&quot;&gt;TraceTogether&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How would this work? If you have any symptoms and self-isolate you let the app know. You are immediately sent a home testing kit, and if you test negative you can leave self-isolation (this is a strong incentive to have the app, even in a country where we value our freedoms). If you test positive all your contacts identified by the app are ordered to self-isolate. The good news is that many will only need to self-isolate for 24h and the rest will get early warning of what they have, and perhaps proactive treatment if the antivirals prove their worth. Because people are only self-isolating for a short while they are more likely to comply, and because they get testing (free from the government) and proactive treatment they are happy to have the app. We collectively manage to contain the spread more effectively, whilst getting more people to work and enjoying life.&lt;/p&gt;

&lt;p&gt;Sure life wouldn’t be back to normal, but we could make social distancing and self-isolation more effective and less costly through effective testing and technology. People could tolerate this for longer (perhaps indefinitely) and the government would get better data to make better decisions. As infection rates went down we could either try and go for broke and eliminate the virus from our shores, or we could relax social distancing to speed to herd immunity whilst knowing exactly when we have to reintroduce more stringent measures,&lt;/p&gt;

&lt;p&gt;My prediction is that without this technology that the self-control (and even police enforced control) required for one to two years before herd immunity or a vaccine arrives will be too tough, and we’ll end up with uncontrolled spikes. If on the other hand we get smart with testing and technology we can reduce the impact of social distancing and self-isolation whilst getting herd immunity in a managed way, and hopefully protecting the most vulnerable through an early vaccine that might be too risky for the young and healthy.&lt;/p&gt;

</description>
        <pubDate>Sun, 22 Mar 2020 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/2020/03/22/covid-19-is-technology-and-testing-an-exit-strategy.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/2020/03/22/covid-19-is-technology-and-testing-an-exit-strategy.html</guid>
        
        
      </item>
    
      <item>
        <title>Flashing One Travel Router From Another</title>
        <description>&lt;p&gt;layout: post
title: “Using One Travel Router to Flash Another”
categories: technology&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I have two travel routers both running OpenWRT connected to each other via an ethernet cable, and on
when the second router starts it pings 192.168.1.2 to see if it alive, and if it
is it downloads a new flash image from a TFTP server at the same address.&lt;/p&gt;

&lt;p&gt;Running the TFTP server is simple following &lt;a href=&quot;https://github.com/alghanmi/openwrt_netgear-wndr3700/wiki/TFTP-Server-on-Your-OpenWRT-Router&quot;&gt;these instructions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The challenge though is that I don’t know what IP address I might be connected to or from, and the most likely source value is 192.169.1.1 which is also my wireless network router
(which is how I connect to travel both my routers)&lt;/p&gt;

&lt;p&gt;Some research showed that it is possible to listen for “anyIP” in a subnet using something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ip route add local 192.168.1.0/24 dev lo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However as this clashes with my wireless router I had to pop it into a table and use IP rules
to treat traffic from br-lan differently. My first attempts to achieve this failed in ways I &lt;a href=&quot;https://superuser.com/questions/1504888/route2-anyip-fails-when-not-in-local-table&quot;&gt;still don’t understand&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In testing this I leaned about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rp_filter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arp_filter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;route_localnet&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src_valid_mark&lt;/code&gt;, all of which I ended up setting thus:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sysctl -w net.ipv4.conf.all.arp_filter=0
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.all.route_localnet=1
sysctl -w net.ipv4.conf.all.src_valid_mark=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even with these changes it didn’t work so I reverted to my old friend DNAT (in conjunction
with the ip route &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip route add local 192.168.1.0/24 dev lo table 100&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sysctl&lt;/code&gt; settings above).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iptables -t nat -A PREROUTING -d 192.168.1.0/24 -i br-lan -j DNAT --to-destination 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This certainly seemed to get the packets processed, but no responses. Using marks I can make it work (the order is important):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
iptables -t mangle -I POSTROUTING  -m conntrack --ctstate DNAT --ctorigdst 192.168.1.0/24 -j MARK --set-xmark 0x64/0xffffffff
iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was under the impression that DNAT should work in reverse, but it dindidn’t seem to. Maybe because we’re using multiple rule tables.&lt;/p&gt;

&lt;p&gt;For now I have decided to use the uBoot feature to use dhcp to accept an IP address, which seems to work just fine.&lt;/p&gt;
</description>
        <pubDate>Sat, 12 Jan 2019 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/2019/01/12/flashing-one-travel-router-from-another.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/2019/01/12/flashing-one-travel-router-from-another.html</guid>
        
        
      </item>
    
      <item>
        <title>Brute Forcing My Own Texecom Premier Elite</title>
        <description>&lt;p&gt;&lt;img src=&quot;/assets/alarm/texecom.png&quot; alt=&quot;Wintex running on Linux&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I live in a new build house, which came with a Texecom Elite 48 alarm system fitted. After a couple of years of use I got an error code that I couldn’t reset with my “master user” code. A few emails to the company who installed the alarm let me know that they had helpfully not handed over the code when they commissioned the system, and then deleted the code when I didn’t pay for extended service. Not impressed!&lt;/p&gt;

&lt;p&gt;They let me know they could come out (for a big fee) and reset the entire alarm system, including the codes. By this point I was grumpy with them, and determined to show them there was an alternative. What I really wanted to do was email them and tell them the code &lt;em&gt;they&lt;/em&gt; had set on &lt;em&gt;my&lt;/em&gt; system.&lt;/p&gt;

&lt;p&gt;My suspicion was the device wouldn’t have any effective protection against brute-forcing at the serial/UDL layer - and that even if it did I was in no worse a position that I had been already so I started work.&lt;/p&gt;

&lt;p&gt;First I needed to get access to the device itself, so having removed the case I tested with a FTDI cable I had lying around using the excellent instructions from &lt;a href=&quot;https://cybergibbons.com/alarms-2/programming-a-texecom-premier-elite-12-w-using-a-ftdi-cable/&quot;&gt;this blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Almost immediate success, but I found it difficult to use a laptop leaning into the cupboard under the stairs where the alarm is. I wanted to control it from my Linux PC upstairs.&lt;/p&gt;

&lt;p&gt;Another &lt;a href=&quot;https://gw0udm.wordpress.com/2015/04/16/texecom-com-ip-controller-diy-and-cheap-too/&quot;&gt;excellent blog&lt;/a&gt; suggested a raspberry pi zero, so I dug one out the cupboard and I thought I’d be ready to start. However around this time my FTDI cable stopped working, probably because of &lt;a href=&quot;https://hackaday.com/2014/10/22/watch-that-windows-update-ftdi-drivers-are-killing-fake-chips/&quot;&gt;FTDIgate&lt;/a&gt; so I was temporarily set-back. I had spare 3.3v FTDI cables but not any 5v ones that the alarm needed so I ordered a couple of multi-voltage ones from Ebay (I reckoned there was a decent chance each wouldn’t work) and looked for temporary alternatives.&lt;/p&gt;

&lt;p&gt;I knew the GPIO pins on the pi were meant to be 3.3v but I thought there was a chance that they’d work on the alarm. I went through the usual fuss to disable the serial console and enable the serial cable and figure out which of the two ports it was running on and started getting some promising but slightly intermittent results. Because of what I wanted to do I decided to wait for the 5v FTDI cable which would be more reliable.&lt;/p&gt;

&lt;p&gt;When it arrived I set up the PI with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ser2net&lt;/code&gt; running as discussed and was quickly able to connect from Wintex running on my Windows device over my home wifi network. However I needed to brute force the connection I would need to figure out the protocol, so I enabled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ser2net&lt;/code&gt; logging and watched what happened as I tried to log on with a bunch of guessed UDL codes. For example the code 1801 generated the following.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Wintex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;03 5A A2 07 5A 31 38 30 31 D4 07 4F 00 16 78 01 1A&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Alarm: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0B 5A 05 01 00 00 01 05 09 00 85 03 06 F6 03 0F ED&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unable to see in what order these characters were being sent and received I dropped into Java on my Linux box and replayed the sent codes in order, with a 50ms delay after each. The let me see the composition of the requests and responses&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Send 1: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;03 5A A2&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Response 1: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0B 5A 05 01 00 00 01 05 09 00 85&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Send 2: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;07 5A 31 38 30 31 D4&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Response 2: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;03 06 F6&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Send 3: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;07 4F 00 16 78 01 1A&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Response: 3 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;03 0F ED&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With a little experimentation I could see that ‘Send 2’ contained the UDL code in ASCII with two bytes leading and one trailing. But what were they? With enough examples I figured out that the trailing digit was a checksum (the sum of each of the digits in the UDL code, subtracted from 222 for four digits codes and 124 for six digit codes). The first two digits were simply the length of the UDL + 3, and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5A&lt;/code&gt;. At this stage I could see that Send 1 and 2 followed the same pattern of total length of string followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5A&lt;/code&gt; so I felt confident I was on the right track.&lt;/p&gt;

&lt;p&gt;I knocked up some ugly but effective code, which let me take arbitrary 4 or 6 digit guesses and generate the codes for ‘Send 2’&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;makeHexString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getIntAsIntArrayOfDigit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checksum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;222&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;checksum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;98&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%02d&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; 5a &quot;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toHexString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%0&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toHexString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checksum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getIntAsIntArrayOfDigit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()]);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From here it was easy, iterate through all 4 and 6 digit codes (I was pretty sure it was one of these) and wait to see if any of the responses changed.&lt;/p&gt;

&lt;p&gt;I ran it for a few hours and started getting different results. I couldn’t log on directly with the code I had found, and suspected I may have hit a defence against brute-forcing, but at over 3000 codes tried at that stage I thought it unlikely. I left the alarm for 24 hours to see if it recovered but I didn’t so I removed the batter and powered it down. I re-ran my code from a few codes before where it stopped and it quickly halted. I tried the new number it had stopped on and boom I was in. :-)&lt;/p&gt;

&lt;p&gt;I must admit I logged in and out a few times to prove I had it, before taking great pleasure in emailing the alarm company to let them know I didn’t need their expensive call-out and that I knew their code, in case they had used it for other premises.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/alarm/pi.jpg&quot; alt=&quot;Pi connected to texecom alarm picture&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It was just a matter now if tidying up. I changed the codes and a few settings. Ordered some unofficial proximity tags (now I had full control of my own system) and reset the warnings. I also decided that I’d like to keep remote control of my alarm so decided to keep the raspberry pi connected. I gently closed the case over the FTDI cable (which was tricky) and left it hanging. I also installed Wintex on Linux using wine and playonlinux (no problems).&lt;/p&gt;

&lt;p&gt;That’s pretty much it. I plan to go back and test the GPIO pins again and see if i can liberate my FTDI cable, and ideally I’d draw power from the 12v inside the alarm so I can leave the pi inside rather than outside the case. (I’ve ordered a 12v to 5v adapter for a couple of quid to do that with)&lt;/p&gt;

&lt;p&gt;Ideally I’d like to remotely monitor the alarm from my phone, but that’s a whole new challenge.&lt;/p&gt;
</description>
        <pubDate>Thu, 10 Jan 2019 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/2019/01/10/brute-forcing-my-own-texecom-premier-elite.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/2019/01/10/brute-forcing-my-own-texecom-premier-elite.html</guid>
        
        
      </item>
    
      <item>
        <title>Reinventing Organisations Review</title>
        <description>&lt;p&gt;I’ve just finished reading Reinventing Organisations, by Frederic Laloux, and I want to recommend it to you all, and I don’t say that lightly. I read quite a few books on leadership and organisational change, and this is probably the best and most progressive.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/dp/2960133501/ref=sr_1_1?ie=UTF8&amp;amp;qid=1440945851&amp;amp;sr=8-1&amp;amp;keywords=reinventing+organisations&quot;&gt;http://www.amazon.co.uk/dp/2960133501/ref=sr_1_1?ie=UTF8&amp;amp;qid=1440945851&amp;amp;sr=8-1&amp;amp;keywords=reinventing+organisations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a nutshell, the book takes a bunch of frustrations about the failings of typical organisations and characterises them as symptoms of our current paradigm, rather than the usual assumption that they represent poor implementations of an inherently good (or at least unquestioned paradigm). In a single book it covers almost every topic (tale portfolio management, and performance related pay as two good examples) that has been discussed and helps show that they are symptoms of the same problem, and not truly independent things as we may have assumed.&lt;/p&gt;

&lt;p&gt;The book starts by setting out a framework (which I’ll share in a moment) for describing different organisational paradigms, before looking intently at the differences between the current prevailing paradigm and the one advocated by the book, using 12 inspiring case studies. At this stage of the book it’s basically boiling frogs on steroids, with a more structured and academic approach. The middle of the book got a bit “happy clappy” for me (lots of talk of meditation and wholeness) before delivering some really cutting advise on how to realise the progressive organisation he describes towards the end of the book.&lt;/p&gt;

&lt;p&gt;The framework that the book uses is to order to the different paradigms used to organise people over time, and their strengths and limitations. Each is given a name and a colour and discussed in some detail. The table here gives a very brief summary:&lt;/p&gt;

&lt;p&gt;[&lt;img src=&quot;https://orghacking.files.wordpress.com/2014/10/reinventingorgs.png&quot; alt=&quot;colouring org types&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Even looking back over the 15 or so years I’ve been at work I can see a transition from a mostly amber (with hints of red) culture to a mostly amber (with bits of green) culture. In other words we’ve come a long way.&lt;/p&gt;

&lt;p&gt;The next state is called “Evolutionary teal” and is where I guess the book will divide readers. Up until now the point has been (somewhat indisputably) that have had several different paradigms for organising ourselves, not just the current one, which helps make it more transient.. Teal is described (in detail, and in contrast to orange and green) using topics such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;organisational structure&lt;/li&gt;
  &lt;li&gt;stuff functions&lt;/li&gt;
  &lt;li&gt;coordination&lt;/li&gt;
  &lt;li&gt;projects&lt;/li&gt;
  &lt;li&gt;decision making&lt;/li&gt;
  &lt;li&gt;information flow&lt;/li&gt;
  &lt;li&gt;conflict resolution&lt;/li&gt;
  &lt;li&gt;performance management&lt;/li&gt;
  &lt;li&gt;compensation&lt;/li&gt;
  &lt;li&gt;dismissal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In one sense the discussion around each of these topics is not particularly novel for those that have heard about Gore, Semco, Zappos, etc. but what the book does is a) give a convincing description of the faults of the current normal paradigm, b) convince you (through several different examples) that there are different ways and c) pull the changes into a coherent framework.&lt;/p&gt;

&lt;p&gt;I would love to retell the stories here, but I don’t think I’ll do the book credit. Instead I’ll provide a couple of quotes and leave you to read the book, and if you don’t have time to do that at least watch the youtube video (https://youtu.be/gcS04BI2sbk)&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If the notion of trusting the collective intelligence of a system seems risky or outright foolish, think about this: the idea that a country’s economy would best be run by the heavy hand of central planning committees in Soviet style has been totally discredited. We all know that a free-market system where a myriad of players pick up on signals, make decisions, and coordinate among themselves works much better. Yet for some strange reason, inside organizations, we still trust the equivalent of central planning committees. Self-management brings the principles that account for successful free-market economies inside organizations.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“There is no master plan. There are no project charters and no one bothers with staffing people on projects. Project teams form organically and disband again when work is done. Nobody knows if projects are on time or on budget, because for 90 percent of the projects, no one cares to put a timeline on paper or to establish a budget. A huge amount of time is freed by dropping all the formalities of project planning—writing the plan, getting approval, reporting on progress, explaining variations, rescheduling, and re-estimating, not to mention the politics that go into securing resources for one’s project or to find someone to blame when projects are over time or over budget”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Buurtzorg has incredibly motivated employees (it is regularly elected “best company to work for” in the country) but, like many other Teal Organizations, it has no human resources department”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Or take the budget process that sets everyone’s objectives, a critical piece in the puzzle to give people room to maneuver. It makes perfect sense in principle. But anyone who has gone through such a process knows how quickly it starts breaking down. When top management asks departments to make their budgets, people play a game called sandbagging—they push for the lowest possible expectation to make sure they will achieve the targets and collect their bonuses. When the numbers don’t add up, top management arbitrarily imposes higher targets (which they make sure exceed what they promised to shareholders, to ensure they will make their bonuses too), which people lower down have no choice but to accept. Instead of frank discussions about what’s feasible and what’s not, people exchange spreadsheets with fictive forecasts driven by fear of not making the numbers. In the process, budgets fail to deliver on one of their key objectives: making people feel accountable and motivated for their outcomes.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“At FAVI, a simple but powerful relief valve exists, should a team leader find the taste of power too sweet: workers can choose at any moment to join another team. Team leaders have no meaningful way of coercing people into desired behavior; they certainly don’t have the authority to fire people unilaterally. If they start to behave autocratically, people can simply walk away”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“targets are problematic for at least three reasons: they rest on the assumption that we can predict the future, they skew our behavior away from inner motivation, and they tend to narrow our capacity to sense new possibilities.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For those that have some natural scepticism about this (which I think it’s important to retain) the most important point made time and time again in the book, is that this isn’t about anarchy or chaos. Everything is still managed, it’s just that the way those things are managed is mostly not about command and control but instead about social institutions. By way of example is someone likely to work harder to meet a goal set in their annual objectives or to keep a promise they made personally to a colleague? For me the answer is obvious, and that is really the message of the book – one organisational function at a time.&lt;/p&gt;
</description>
        <pubDate>Sat, 05 Nov 2016 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/2016/11/05/reinventing-organisations-review.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/2016/11/05/reinventing-organisations-review.html</guid>
        
        
      </item>
    
      <item>
        <title>D3 Venn with Constrained Children</title>
        <description>&lt;p&gt;I came across &lt;a href=&quot;https://github.com/benfred/venn.js/&quot;&gt;d3 venn&lt;/a&gt; and had the idea of displaying the categorisation of nodes by placing them in the appropriate circle or intersection. In this way the size of each venn would be defined by the number of items within it.&lt;/p&gt;

&lt;p&gt;Whilst figuring out how to do this I came accross &lt;a href=&quot;https://github.com/christophe-g/d3-venn&quot;&gt;https://github.com/christophe-g/d3-venn&lt;/a&gt; which was close to what I wanted &lt;a href=&quot;http://bl.ocks.org/christophe-g/b6c3135cc492e9352797&quot;&gt;visually&lt;/a&gt;, but I wasn’t that keen on the circle packing (and wasted space) and also found what seemed to be a &lt;a href=&quot;https://github.com/christophe-g/d3-venn/issues/3&quot;&gt;bug&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I decided to have a tinker and see if I could get it work how I wanted, and also as an opportunity to learn javascript and d3. Before I explain what I did (and how I did it) take a look at the result:&lt;/p&gt;

&lt;iframe src=&quot;/assets/d3_venn_with_children/dynamic_mod.html&quot; frameborder=&quot;0&quot; width=&quot;100%&quot; height=&quot;800&quot;&gt; &lt;/iframe&gt;

&lt;p&gt;As you’ll see each child node is rendered in the colour of its category and is pulled strongly (almost constrained) into the correct circle or intersection. If it is dragged out of its rightful place then it turns black to indicate the problem. The number of nodes in each circle or intersection can be changed, but to get it working I did remove the smooth animation on these. Maybe something to fix in due course.&lt;/p&gt;

&lt;p&gt;The children nodes are all laid out together, as part of the same force layout. They are each pulled towards the centre of the correct circle or intersection, as well as having gravity towards each other. There is also a force to stop them overlapping.&lt;/p&gt;

&lt;p&gt;The trickiest bit has been figuring out the bounding area of the circles. The original version had circles represented by their whole area, without any intersecting area removed. E.g. circle A in set A,B,C was defined as the whole area of A, without the removal of the intersections AB and AC and ABC. Whilst it was fairly straight-forward to create a path that chose different arcs I remain unsure about all the edge cases, in particular circles that have “impinging” circles that extend wider than the midpoint of the circle.&lt;/p&gt;

&lt;p&gt;Now for the code, hosted as a &lt;a href=&quot;https://gist.github.com/leocrawford/28d8b469c576871c46edef48e1863ddd&quot;&gt;gist&lt;/a&gt;&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/28d8b469c576871c46edef48e1863ddd.js&quot;&gt; &lt;/script&gt;

</description>
        <pubDate>Sat, 29 Oct 2016 00:00:00 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/2016/10/29/d3-venn-with-children.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/2016/10/29/d3-venn-with-children.html</guid>
        
        
      </item>
    
      <item>
        <title>Operating Model: The Magna Carta for Organisations?</title>
        <description>&lt;h1 id=&quot;operating-model-the-magna-carta-for-organisations&quot;&gt;Operating Model: The Magna Carta for Organisations?&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/operating_model/magna_carta.jpg&quot; alt=&quot;magna carta picture&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You’ve no doubt heard of the Mangna Carta (1215). It established the principle that the king was subject to and not above the law, enshrined the right of fair trial in law, standardised weights and measures&lt;sup id=&quot;fnref:weights&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:weights&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and (#dontmentionbrexit) provided for freedom of travel and freedom of trade:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;All merchants may leave or enter England in safety and security. They may stay and travel throughout England by road or by water, free from all illegal tolls, in order to buy and sell according to the ancient and rightful customs. This is except, in time of war, those merchants who are from the land at war with us. And if such merchants are found in our land at the beginning of the war, they shall be detained, without injury to their bodies or goods, until information is received by us (or by our chief justiciar) about in what way are treated our merchants, thence found in the land at war with us . If our men are safe there, the others shall be safe in our land.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;It shall be lawful in future for any one, keeping loyalty to the Crown, to leave our kingdom and to return safely and securely, by land and by water. This is except in time of war, when men may go, only in the public interest, for some short period. (This excludes, always, those imprisoned or outlawed in accordance with the law of the realm, natives of any country at war with us, and merchants, who shall be treated as previously stated).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whilst the Magna Carta has had a fairly rocky 800 year history; being annulled within months of being issued, and many of the clauses being removed or softened over time it has not only shaped our liberal democracy, but (arguably) more importantly has started a shift from Customary Law to written Statutory Law with prescribed mechanisms for judgment and enforcement.&lt;/p&gt;

&lt;p&gt;In other words, it was a short document that transformed the governance of the country; or in the words of the Magna Carta itself initiated a “reform of our realm”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;…and the advancement of the holy Church, and for the &lt;strong&gt;reform of our realm&lt;/strong&gt;, by advice of our venerable fathers…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/operating_model/genghis_kahn.jpg&quot; alt=&quot;genghis kahn picture&quot; style=&quot;float:right;clear:both;padding-bottom:10px;padding-left:10px;width:250px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s hard to think of a written document changing a whole way of living, but the Magna Carta is by far not the only example. Indeed, preceding it by nine years the The Yasak of Genghis Khan set out some simple rules (and punishments) of the land, enshrined freedom of religion, and set out rules for his successor to be appointed - all despite being illiterate himself.&lt;/p&gt;

&lt;p&gt;The impact was immense:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Petty wars and the sudden, ferocious raids of neighbors disappeared. Banditry almost vanished. The ordinary tribesman, travelling across those endless pastures with his tent, his wagon, his family and his flocks, a new peace now brought him comfort.” [Peter Brent, Genghis Khan]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;http://206hwf3fj4w52u3br03fi242.wpengine.netdna-cdn.com/wp-content/uploads/2009/10/hughMcLeodCompanyHierarchy.jpg&quot; alt=&quot;MacLeod’s Archetypes&quot; style=&quot;float:right;clear:both;padding-bottom:10px;padding-left:10px;width:250px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Most organisations today may not have banditry, but are operating much like countries did before the Magna Carta (and the Yasak); the freedoms and responsibilities of individuals are passed down from one generation to the next rather than shaped to solve the problems of the day creating a competitive and political environment which serves neither the people who work in them, or those the organisation is meant to serve. For a disturbing picture of the sort of people that these organisations create check out MacLeod’s Archetypes.&lt;/p&gt;

&lt;div style=&quot;clear:both;&quot; /&gt;

&lt;p&gt;Today’s organisations function instead using a legacy Operating Model (because that is the name we give to these ways of workings) that is based on ideas from over a century ago, and is shared not as a document (which can be challenged and updated) but is transmitted culturally. In order to understand today’s orgainisations it is worth understanding the key influences that made them the way they are - and which are unconsciously culturally transmitted (infected) from one generation to the next. Meet Frederick Taylor’s “Principles of Scientific Management” and Max Weber’s work on Bureaucracy:&lt;/p&gt;

&lt;p&gt;Taylor advocated:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/operating_model/scientific_management.jpg&quot; alt=&quot;scientific management&quot; style=&quot;float:right;clear:both;padding-bottom:10px;padding-left:10px;width:150px;&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;Replace working by “rule of thumb,” or simple habit and common sense, and instead use the scientific method to study work and determine the most efficient way to perform specific tasks.&lt;/li&gt;
    &lt;li&gt;Rather than simply assign workers to just any job, match workers to their jobs based on capability and motivation, and train them to work at maximum efficiency.&lt;/li&gt;
    &lt;li&gt;Monitor worker performance, and provide instructions and supervision to ensure that they’re using the most efficient ways of working.&lt;/li&gt;
    &lt;li&gt;Allocate the work between managers and workers so that the managers spend their time planning and training, allowing the workers to perform their tasks efficiently.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;and Weber:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/operating_model/max_weber.jpg&quot; alt=&quot;max weber&quot; style=&quot;float:right;clear:both;padding-bottom:10px;padding-left:10px;width:300px;&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Bureaucracies are organized according to rational principles. Offices are ranked in a hierarchical order and their operations are characterized by impersonal rules. Incumbents are governed by methodical allocation of areas of jurisdiction and delimited spheres of duty. Appointments are made according to specialized qualifications rather than ascriptive criteria.&lt;sup id=&quot;fnref:weber&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:weber&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yet Weber also noted the dysfunctions of bureaucracy;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Its major advantage, the calculability of results, also makes it unwieldy and even stultifying in dealing with individual cases.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Weber’s ideal bureaucracy is characterized by the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Hierarchical organization&lt;/li&gt;
  &lt;li&gt;Delineated lines of authority with fixed areas of activity&lt;/li&gt;
  &lt;li&gt;Action taken on the basis of, and recorded in, written rules&lt;/li&gt;
  &lt;li&gt;Bureaucratic officials with expert training&lt;/li&gt;
  &lt;li&gt;Rules implemented by neutral officials, and&lt;/li&gt;
  &lt;li&gt;Career advancement depending on technical qualifications judged by organization, not individuals&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a great deal to like in these two visions of organistional operation. Indeed the first three of Taylor’s points look pretty solid, as do the last three of Weber’s ideals. A good case could be made that we don’t do these things well enough in organisations today.&lt;/p&gt;

&lt;p&gt;The three traps that I think they lead us into however:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;That managers and workers should be seperate&lt;/strong&gt;. It’s been years since we lost the idea of blue collar and white collar staff, dressed differently, with fundementaly different roles and privilages but the thinking behind it seems to remain in place. I think this is a legacy of manufacturing where there was an intrinsic seperation between the design and the production; there was literally no way that a worker on the Ford Mondeo production line could make a few tweaks and end up with a Focus instead. Regardless of how much autonomy you chose to gave workers about how they did their jobs, there was no wiggle room on what that produced. That distinction simply doesn’t exist for modern information workers where it is rarely possible to meet an entire specification, and thus a key role of every “worker” is to bring their skill to bear in helping to decide what is possible, and to negotiate changes to what is required to make it more achievable. In this sense every worker is a manager too, and arguably the most useful managers are those who still do “real” work and are most connected to the practicalities of delivering value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/operating_model/cynefin.png&quot; alt=&quot;cynefin&quot; style=&quot;float:right;clear:both;padding-bottom:10px;padding-left:10px;width:250px;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Action can be pre-determined&lt;/strong&gt;.  Complexity theory gives us some very useful language to understand the applicability of Weber’s second ideal. The application of pre-determined rules clearly relates to the &lt;em&gt;Obvious&lt;/em&gt; quadrant of the &lt;a href=&quot;https://www.youtube.com/watch?v=N7oz366X0-8&quot;&gt;Cynefin model&lt;/a&gt;, where we expect a &lt;em&gt;sense-categorise-respond&lt;/em&gt; cycle which corresponds with rule-based activity. However as work becomes complicated and then complex we move to &lt;em&gt;sense-analyse-respond&lt;/em&gt; (use skill over written rule) and &lt;em&gt;probe-sense-respond&lt;/em&gt; (experiment) to choose our course of action.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Work can and should be partitioned&lt;/strong&gt;. One of the things that Systems Engineers get trained to do is splitting a problem up into “Mutually Exclusive, Collectively Exhaustive” parts. What they don’t get trained to do is figure out what do do when that’s not possible. Conversely Systems Thinking advocates looking at a system as a set of interconnected parts, which can’t be easily split apart from each other. Again, context is everything. In the domain on the complicated (so-called “hard systems engineering”) things can be split into parts (like a submarine is made of bits). In soft-systems approaches we look at things as ecosystems, which tend to be created through evolutionary approaches, and which can’t be split down into its constituent parts without breaking. Whilst it’s possible to take a submarine to bits and put it together, you can’t really do that with a bird or an ocean. It just doesn’t make sense.  Likewise try and decide where one cloud stops and another starts.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope that these three issues illustrate the fact that the way we run businesses is contextual; and that our changing problem means that todays organisations might need to adapt their models to suit their need. For example Amazon uses a hierarchial structure to manage their warehouses and distribution, but a network structure to manage their software development.&lt;/p&gt;

&lt;p&gt;To change our ways of working we first need to understand the assumptions and history of our current models so we know how to change them. It is for this reason that I advocate organisations start by understanding how their current Operating Model works before trying to  change it. Having the Opertaing Model in written form will make it easier to change as well as making it clear to all the deficiences we accept in our routine, which are made clear when we see them written down.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:weights&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;About weights and measures it reads (all translations from http://www.magnacartaplus.org/magnacarta/]:&lt;/p&gt;

      &lt;blockquote&gt;
        &lt;p&gt;There shall be one measure of wine, of ale and of corn (namely, “the London
quarter”) throughout our whole realm. There shall also be one width of cloth
(whether dyed, russet, or halberget): that is, two ells within the selvages.
Let weights also be standardised similarly.&lt;/p&gt;
      &lt;/blockquote&gt;
      &lt;p&gt;&lt;a href=&quot;#fnref:weights&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:weber&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Source: Boundless. “Weber’s Model for Bureaucracy.” Boundless Sociology. Boundless, 08 Aug. 2016. Retrieved 31 Aug. 2016 from https://www.boundless.com/sociology/textbooks/boundless-sociology-textbook/social-groups-and-organization-6/bureaucracy-56/weber-s-model-for-bureaucracy-352-10202/ &lt;a href=&quot;#fnref:weber&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 06 Oct 2016 12:31:25 +0000</pubDate>
        <link>http://www.leocrawford.org.uk/organisations/2016/10/06/why-operating-model.html</link>
        <guid isPermaLink="true">http://www.leocrawford.org.uk/organisations/2016/10/06/why-operating-model.html</guid>
        
        
        <category>organisations</category>
        
      </item>
    
  </channel>
</rss>
