Hacking on Arlec Christmas lights with tasmota

I’m loving the wide array of electrically certified home automation devices we’re seeing now. Light bulbs, sensors, power boards, and even Christmas lights. Specifically Arlec is shipping these app controllable Christmas lights this year, which looked very much like they should work with Tasmota.

(Sorry for the terrible product picture, I can’t find this product online any more, I suspect Bunnings has sold out for the year?)

Specifically, it turns out that these Arlec lights are an ESP8266 which can be flashed with tuya-convert v2 to run tasmota. Once flashed, you can control all of the functions available on the device itself, although there are parts of the protocol I haven’t fully understood yet.

Let’s start off by flashing the device:

  • First off boot your raspberry pi with tuya-convert. I used v2, and I suspect that’s important here so make sure you upgrade if you’re using something old.
  • Next, put the bud lights into programming mode by holding the button on the control box down until the light strand turns off. Release and the strand should start blinking every couple of seconds.
  • Now run the tuya-convert flashing script.
  • Now go to the tasmota-XXXX essid and enter your wifi details into the captive portal. The light strand will now reboot.
  • The light strand should now appear on your wifi, and you can find the IP address and MAC address by asking your DHCP server nicely.

Now for some basic configuration in the web UI. Set the module type to “Tuya MCU (54)”, GPIO1 to “Tuya Tx (107)”, and GPIO3 to “Tuya RX (108)”. You’ll also want to set the usual site-specific configuration options like your MQTT server and so forth.

So what is a “Tuya MCU” when it is at home? Well, it turns out that some tuya devices have an esp8266 which just talks serial to another microcontroller. It kind of makes sense if you already have a microcontroller device you want to make “smart” and you’re super dooper lazy I suppose. There is surprisingly good manufacturer documentation online.

In the case of these bud lights, I have a strong theory that we can basically cycle the modes available by pressing the physical button, but I needed a way to validate that.

You can read more about how these devices work on the tuya protocols page, or you can just jump ahead to the simple programs I wrote to explore these devices. However, a quick summary of the serial protocol spoken between the esp8266 and the MCU is helpful. Packets look like this:

  • Frame header: fixed 2 byte value 0x55aa
  • Version: 0x00
  • Command word: a byte
  • Data length: 2 bytes
  • Function length: 2 bytes
  • Function command: 1 byte
  • Checksum: 1 byte

First off I wrote a simple program to monitor the state of the device registers (called dpIds for “define product ids”). It just uses the tasmota web console to constantly ask for the current state of the dpIds (“SerialSend5 55aa0001000000”, a hard coded MCU control packet) and prints out any changes. Note that for it to work, the web console log level needs to be set to debug.

Now I can press the button on the device and see what dpIds change. A session looked like this (the notes like “solid white” are things I typed into the terminal as I went):

Clearly dpId 1 (type 1, a boolean) is the power state with 0 being off and 1 being on. This is also easily testable of course. If you send a “TuyaSend1 1,0” to the device using the web console it turns off, and if you send “TuyaSend1 1,1” it turns on. This assignment also maps to the way other Tuya MCU devices are configured, so it seems like a very safe assumption.

dpId 101 (type 4, 1 byte enum) seems to be the mode, walking through the possible values determined:

  • 0: fast pulse
  • 1: twinkle
  • 2: alternate
  • 3: alternate differently
  • 4: alternate and cause epileptic fits
  • 5: double alternate
  • 6: flash
  • 7: solid
  • 8: off
  • 9 onwards: brief all

dpId 107 (type 2, 4 byte value) wasn’t changable via the physical button, so I wrote another simple script to send a bunch of values to it. It appears to be a brightness control for the white LEDs, with 0 being off and 99 being fully on. I haven’t managed to find a brightness control for the coloured LEDs. The brightness control also doesn’t appear to work in all modes.

dpId108 (type 2, 4 byte value) remains a mystery to me at this time. It doesn’t seem to change regardless of what values I send it.

dpId 109 (type 4, 1 byte enum) seems to be which strand is on. A value of 0 is just white LEDs, 1 is just coloured LEDs, 2 is all LEDs, and 3 is all LEDs but dimmer.

It sort of doesn’t matter that I haven’t fully decoded the inner workings of the device, because this is enough information for my use case. All I really want is for all the lights to be on solidly (that is, with no blinking). This is because I use them for lighting under my back pergola and the blinking would be quite annoying.

So how do you wire up Home Assistant to send serial packets to a slave MCU over MQTT? Home Assistant will already control the lights turning on and off because of the default relay implementation for dpId 1. What I need to be able to do is turn on all the lights in a solidly on configuration. For that, I can implement rules like:

>> Rule1
ON Event#0 DO TuyaSend4 101,0 ENDON
ON Event#1 DO TuyaSend4 101,1 ENDON
ON Event#2 DO TuyaSend4 101,2 ENDON
ON Event#3 DO TuyaSend4 101,3 ENDON
ON Event#4 DO TuyaSend4 101,4 ENDON
ON Event#5 DO TuyaSend4 101,5 ENDON
ON Event#6 DO TuyaSend4 101,6 ENDON
ON Event#7 DO TuyaSend4 101,7 ENDON
ON Event#8 DO TuyaSend4 101,8 ENDON
ON Event#9 DO TuyaSend4 101,9 ENDON

>> Rule1 ON

>> Rule2
ON Power1#state=1 DO TuyaSend1 1,1 ENDON
ON Power1#state=1 DO TuyaSend4 109,2 ENDON

>> Rule2 ON

You apply this rule by pasting it into the web console on the device. Note that there are four separate pasted commands. Rule1 exposes the modes from dpId101 as effects in Home Assistant, and Rule2 hooks to the power on MQTT command to ensure that the lights are set to solidly on via dpId 109.

The matching Home Assistant configuration looks like this:

# Arlec fairy lights on the back deck
- platform: mqtt
  name: "Back deck 1"
  command_topic: "cmnd/sonoff14/POWER"
  state_topic: "tele/sonoff14/STATE"
  state_value_template: "{{value_json.POWER}}"
  availability_topic: "tele/sonoff14/LWT"
  effect_command_topic: "cmnd/sonoff14/Event"
  effect_list:
    - 0
    - 1
    - 2
    - 3
    - 4
    - 5
    - 6
    - 7
    - 8
    - 9
  payload_on: "ON"
  payload_off: "OFF"
  payload_available: "Online"
  payload_not_available: "Offline"
  qos: 1
  retain: false

This gives me the ability to turn the fairy lights on and off via Home Assistant, and ensure they they’re solidly on and not blinking. That’s good enough for now.

1-Wire home automation tutorial from linux.conf.au 2019, part 3

This is the third in a set of posts about the home automation tutorial from linux.conf.au 2019. You should probably read part 1 and part 2 before this post.

In the end Alistair decided that my home automation shield was defective, which is the cause of the errors from the past post. So I am instead running with the prototype shield that he handed me when I started helping with the tutorial preparation. That shield has some other bugs (misalignments of holes mainly), but is functional apart from that.

I have also decided that I’m not super excited by hassos, and just want to run the orangepi with the OWFS to MQTT gateway into my existing home assistant setup if possible, so I am going to focus on getting that bare component working for now.

To that end, the gateway can be found at https://github.com/InfernoEmbedded/OWFS-MQTT-Bridge, and is a perl script named ha-daemon.pl. I needed to install some dependancies, which in my case were for armbian:

$ apt-get install perl libanyevent-perl cpanminus libdist-zilla-perl libfile-slurp-perl libdatetime-format-strptime-perl
$ dzil listdeps | cpanm --sudo

Then I needed to write a configuration file and put it at ha.toml in the same directory as the daemon. Mine looks like this:

[general]
	timezone="Australia/Sydney"
	discovery_prefix="homeassistant"

[1wire]
	host="localhost"
	port=4304
	timeout=5 # seconds, will reconnect after this if no response
	sensor_period=30 # seconds
	switch_period=10 # seconds
	debug=true

[mqtt]
	host="192.168.1.6"
	port=1883

Now run the gateway like this:

$ perl ha-daemon.pl

I see messages on MQTT that a temperature sensor is being published to home assistant:

homeassistant/sensor/1067C6697351FF_temperature/config {
	"name": "10.67C6697351FF_temperature",
	"current_temperature_topic": "temperature/10.67C6697351FF/state",
	"unit_of_measurement": "°C"
}

However, I do not see temperature readings being published. Having added some debug code to OWFS-MQTT, this appears to be because no temperature is being returned from the read operation:

2019-05-27 17:28:14.833: lib/Daemon/OneWire.pm:73:Daemon::OneWire::readTemperatureDevices(): Reading temperature for device '10.67C6697351FF'
[...snip...]
2019-05-27 17:28:14.867: /usr/local/share/perl/5.24.1/AnyEvent/OWNet.pm:117:Daemon::OneWire::__ANON__(): Read data: $VAR1 = bless( {
                 'payload' => 0,
                 'size' => 0,
                 'version' => 0,
                 'offset' => 0,
                 'ret' => 4294967295,
                 'sg' => 270
               }, 'AnyEvent::OWNet::Response' );

I continue to debug.

1-Wire home automation tutorial from linux.conf.au 2019, part 2

For the actual on-the-day work, delegates were handed a link to these instructions in github. If you’re playing along at home, you should probably read 1-Wire home automation tutorial from linux.conf.au 2019, part 1 before attempting the work described here. Its especially important that you know the IP address of your board for example.

Relay tweaks

The instructions are pretty self explanatory, although I did get confused about where to connect the relay as I couldn’t find PC8 in my 40 pin header diagrams. That’s because the shields for the tutorial have a separate header which is a bit more convenient:

GPIO header

I was also a bit confused when the relay didn’t work initially, but that turns out because I’d misunderstood the wiring. The relay needs to be powered from the 3.3v pin on the 40 pin header, as there is a PCB error which puts 5v on the pins labelled as 3.3v on the GPIO header. I ended up with jumper wires which looked like this:

Cabling the relay

1-Wire issues

Following on the tutorial instructions worked well from then on until I tried to get 1-Wire setup. The owfs2mqtt bridge plugin was logging this:

2019-04-08 19:23:55.075: /opt/OWFS-MQTT-Bridge/lib/Daemon/OneWire.pm:148:Daemon::logError(): Connection to owserver failed: Can't connect owserver: Address not available

Debugging that involved connecting to the owfs2mqtt docker container (hint: ssh to the Orange Pi, do a docker ps, and then run bash inside the docker container for the addon). Running owserver with debug produces this:

owserver fails

Sorry to post that as an image, cut and paste for the hassos ssh server doesn’t like me for some reason. I suspect I have a defective DS2482, but I’ll have to wait and see what Allistair says.

1-Wire home automation tutorial from linux.conf.au 2019, part 1

I didn’t get much of a chance to work through the home automation tutorial at linux.conf.au 2019 because I ended up helping others in the room get their Orange Pi is booting. Now that things have settled down after the conference, I’ve had a chance to actually do some of the tutorial myself. These are my notes so I can remember what I did later…

Pre-tutorial setup

You need to do the pre-tutorial setup first. I use Ubuntu, which means its important that I use 18.10 or greater so that st-link is packaged. Apart from that the instructions as written just worked.

You also need to download the image for the SD card, which was provided on the day at the conference. The URL for that is from github. Download that image, decompress it, and then flash it to an SD card using something like Balena Etcher. The tutorial used 32gb SD cards, but the image will fit on something smaller than that.

hassos also doesn’t put anything on the Orange Pi HDMI port when it boots, so your machine is going to look like it didn’t boot. That’s expected. For the tutorial we provided a mapping from board number (mac address effectively) to IP address allocated in the tutorial. At home if you’re using an Orange Pi that isn’t from the conference you’re going to have to find another way to determine the IP address of your Orange Pi.

The way we determined MAC addresses and so forth for the boards used at the conference was to boot an Armbian image and then run a simple python script which performed some simple checks of each board by logging into the board over serial. The MAC addresses for the boards handed out on the day are on github.

An Aside: Serial on the Orange Pi Prime

As an aside, the serial on the Orange Pi Prime is really handy, especially with the hassos image. Serial is exposed by a three pin header on the board, which is sort of labelled:

Orange Pi Prime Serial Port
The Orange Pi Prime Serial Port

Noting that you’ll need to bend the pins of the serial header a little if you’re using the shield from the conference:

Serial port connected to a USB to serial converter

The advantage being that suddenly you get useful debugging information! The serial connection is 115200 baud 8N1 (8 data bits, no parity, 1 stop bit) by the way.

Serial debug information from an hassos boot

The hassos image used for the conference allows login as root with no password over serial, which dumps you into a hass interface. Type “login” to get a bash prompt, even though its not in the list of commands available. At this point you can use the “ip address” command to work out what address DHCP handed the board.

The actual on-the-day work

So at this point we’re about as ready as people were meant to be when they walked into the room for the tutorial. I’ll write more notes when I complete the actual tutorial.

Brilliant Smart Wifi plug with Tasmota

A couple of weeks ago I was playing with Tuya derived smart light globes (Mirabella Genio from K-Mart Australia in my case, but there are a variety of other options as well). Now Bunnings has the Brillant Smart Wifi Plug on special for $20 and I decided to give that a go as well, given it is also Tuya derived.

The basic procedure for OTA flashing was the same as flashing the globes, except that you hold down the button on the device for five seconds to put it into flash mode. That all worked brilliantly, until I appear to have fat fingered my wifi details in Tasmota — when I rebooted the device it never appeared on my network.

That would be much more annoying on the globes, but it turns out these smart plugs are really easy to open and that Tuya has documented the pin out of the controlling microprocessor. So, I ended up temporarily soldering some cables to the microprocessor to debug what had gone wrong. It should be noted that as a soldering person I make a great software engineer:

Jumper wires soldered to the serial port.

Once you’ve connected with a serial console, its pretty obvious who can’t be trusted to type their wifi password correctly:

I can’t have nice things.

At this point I was in the process of working out how to use esptool to re-flash the plug when I got super lucky. However, first… Where the hell is GPIO0 (the way you turn on flashing mode)? Its not broken out on the pins for the MCU, but as a kind redditor pointed out, it is exposed on a pad on the back of the board:

The cunningly hidden GPIO0.

…and then I got lucky. You see, to put the MCU into flashing mode you short GPIO0 to ground. I was having trouble getting that to work with esptool, so I had a serial console attached to see what was happening. There I was, shorting GPIO0 out over and over trying to get the magic to work. However, Tasmota also setups up a button on GPIO0 by default, because the sonoffs have a hardware button on that pin. If you hit that button with four short presses, you put the device back into captive portal configuration mode

Once I was back in that mode I could just use a laptop over wifi to re-enter the wifi password and I’m good to go. In hindsight I didn’t need the serial port if I could have powered the device and shorted that pin four times, but it sure was nice to be told what was happening on the serial console while poking around.

Mirabella Genio smart lights with Tasmota and Home Assistant

One of the things I like about Home Assistant is that it allows you to take hardware from a bunch of various vendors and stitch it together into a single consistent interface. So for example I now have five home automation vendor apps on my phone, but don’t use any of them because Home Assistant manages everything.

A concrete example — we have Philips Hue lights, but they’re not perfect. They’re expensive, require a hub, and need to talk to a Philips data centre to function (i.e. the internet needs to work at my house, which isn’t always true thanks to the failings of the Liberal Party).

I’d been meaning to look at the cheapo smart lights from Kmart for a while, and finally got around to it this week. For $15 you can pickup a dimmable white globe, and for $29 you can have a RGB one. That’s heaps cheaper than the Hue options. Even better, the globes are flashable to run the open source Tasmota stack, which means no web services required!

So here are some instructions on flashing these globes to be useful:

Buy the globes. I bought this warm while dimmable and this RBG option.

Flash to tasmota. This was a little bit fiddly, but was mostly about getting the sequence to put the globes into config mode right (turn off for 10 seconds, turn on, turn off, turn on, turn off, turn on). Wait a few seconds and then expect the lamp to blink rapidly indicating its in config mode. For Canberra people I now have a raspberry pi setup to do this easily, so we can run a flashing session sometime if people want.

Configure tasmota. This is really up to you, but the globes need to know local wifi details, where your MQTT server is, and stuff like that.

And then configure Home Assistant. The example of how to do that from my house is on github.

Support for Raspberry Pi and Orange Pi GPIOs in Home Assistant

So, I’ve been off in the GPIO library salt mines for a while, but am now ready to circle back and document how to get GPIO inputs and outputs working in Home Assistant. This now works on both Raspberry Pi and OrangePi, assuming that my patch gets merged.

First off, let’s talk about GPIO outputs. This is something which has been working for a while on both platforms (a while being a week or so, assuming you’ve patched Home Assistant with my pull request, but you’re all doing that right?).

To configure an output in Home Assistant, you would add the following to configuration.yaml:

rpi_gpio:
  board_family: orange_pi

switch:
 - platform: rpi_gpio
   ports:
     PA7: LED

Where board_family can be either “raspberry_pi” or “orange_pi”. Note that for Raspberry Pis, the pin numbers are always numbers whereas for OrangePi we are using “SUNXI” numbering, which is of the form “PA7”.

The circuit for this LED is really simple:

A simple LED circuit

Now we have a switch we can control in Home Assistant:

Raspberry Pi LED switch in Home Assistant

GPIO inputs are similar. The configuration looks like this:

rpi_gpio:
  board_family: orange_pi

binary_sensor:
 - platform: rpi_gpio
   invert_logic: true
   ports:
     PA7: PUSHYBUTTON

With a circuit like this:

A circuit with a button in it

invert_logic set to true is required because our circuit sends the value of PA7 to ground when the button is pressed.

A push button being pressed in Home AssistantNoting that sensors look different to switches in Home Assistant, you can see the binary sensor at the top right of the image, with its history being displayed in the dialog box in the foreground.

Pull Requests for the LCA2019 Home Automation tutorial

A quick list of things I did for the LCA2019 Home Automation tutorial. Of course Alistair did a lot more, but I still want to track these.

Further adventures in Home Assistant OrangePi GPIO

Its funny how a single sentence can change your course. In the last post about this work, I said:

We also need to run hass as root,  because OrangePi GPIO support requires access to /dev/mem for reasons I haven’t dug into just yet.

That’s turned out to be (reasonably) a pretty big sticking point upstream. Access to /dev/mem gives you a whole bunch of access to the machine that Home Assistant probably shouldn’t have.

Alastair went off spelunking because he’s more patient than me and found yet another OrangePi GPIO library. I think we’re up to three or four of these at the moment, but this is the first one we’ve found which supports the sysfs interface to GPIO pins. That’s exciting because it removes our run-as-root requirement. Its unexciting in that the sysfs interface has been deprecated by the kernel, but will remain supported for a while.

I think people would be within their rights to conclude that the state of GPIO libraries for OrangePi is a bit of a dumpster fire right now.

Anyways, the point of this post is mostly to write down how to use the sysfs interface to GPIO pins so that I can remember it later, I’ll take more about this new library and if it meets our needs in a later post.

The first step is to determine what pin number the GPIO pin is. On the OrangePi these are labelled with names like “PA7”, which is the 7th bit in the “A” GPIO register. To convert that into a pin number as used by sysfs you do this:

def pin_number(letter, digit):
    return (ord(letter) - ord('A')) * 32 + digit

So, pin_number(‘A’, 7) for PA7 is just … 7.

Note that I now recommend that people use SUNXI pin mapping, as its much less confusing. You can read more about alternative pin mappings in this post of worked OrangePi GPIO examples.

Now we can enable the pin, set it to output, and then blink the LED to prove it works:

# cd /sys/class/gpio
# echo 7 > export
# cd gpio7
# echo "out" > direction
# echo 1 > value
# sleep 1
# echo 0 > value

The next step? To make sure that the new GPIO library supports sysfs access to GPIOs on the OrangePi Prime.

Adventures in Home Assistant Raspberry Pi GPIO

Alastair D’Silva is running what looks to be a very well prepared home automation tutorial at LCA2019 based on Home Assistant. I offered to have a hack on the support for GPIO pins on OrangePi boards in Home Assistant because it sounded interesting for a vacation week. The only catch being that I’d never done anything with GPIO pins at all on either Raspberry Pi or Orange Pi.

A simple LED circuit for a Raspberry PiThe first step seemed to be to get GPIO working at all on a Raspberry Pi (which is currently supported out of the box with Home Assistant). This online tutorial has a simple example of a circuit and the associated python code to blink a LED on a Raspberry Pi, so off I went to build that circuit.

The circuit has a LED with a 330 ohm pull up resistor on GPIO pin 18 on the board. The sample python code on the page above just blinks that LED, which I used to make sure that the circuit as working as intended.

To configure the GPIO pin as a switch in Home Assistant, I added the following to configuration.yaml (noting that the empty rpi_gpio entry isn’t strictly required, but will be later):

rpi_gpio:

switch:
 - platform: rpi_gpio
   ports:
     18: LED

Which leaves me with something like this in the web UI:

Raspberry Pi LED switch in Home Assistant

It even works!

I’ve lied to you a little bit above, for which I apologise. I’ve also been working on helping Alastair with adding Orange Pi to the rpi_gpio component in Home Assistant, as the tutorial is based on Orange Pi Primes, with a custom home automation shield installed. Now that I have a sample configuration that works for Raspberry Pi and a test circuit, its time to make sure that Orange Pi works correctly too.

Home Assistant doesn’t currently have any support for Orange Pi GPIOs. The first approach I took was to forward port this ancient patch which adds Orange Pis as a new component beside Raspberry Pis. That port is available here, but in the end I decided it would be nicer to just have the existing Raspberry Pi component also support Orange Pis, instead of duplicating a whole bunch of code and adding some confusion.

(It should be noted that there are downsides to this new approach — the code is more complicated this way, and Raspberry Pi owners need to download the Orange Pi GPIO library even though they’ll never use it. That said, I see these downsides as relatively minor).

GPIO pin mapping for an Orange PiA small hitch however. Orange Pi names the GPIO ports in a quite different way from how Raspberry Pi does, and this took some time to get used to. The mapping of GPIO pins was a little hard to find, so I’ll include it here (the image to the left). A second hitch was that I needed a linux image for the board. I’ve used Armbian Stretch, as the hass.io image is quite locked down (no ssh to the base OS for example).

Based on the pin image instead of the Pin 18 from the previous example, I moved to what is labelled on the tutorial shield as “PA7”, and which is referred to in code as Pin 29.

The code for blinking is a bit different from the example linked above, so here is a tweaked version:

import OPi.GPIO as GPIO
import time


GPIO.setboard(GPIO.PRIME)
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(29, GPIO.OUT)

while True:
    GPIO.output(29, GPIO.HIGH)
    time.sleep(1)
    GPIO.output(29, GPIO.LOW)
    time.sleep(1)

Note here that we need to specify what board we’re on (in this case a Prime), and we set the mode differently than the linked example.

So now let’s be over achievers and get things working in Home Assistant too! We need a configuration.yaml which includes something like this:

rpi_gpio:
  board_family: orange_pi
  board: prime

switch:
 - platform: rpi_gpio
   ports:
     29: LED

Note the additional config in the rpi_gpio entry. We also need to run hass as root,  because OrangePi GPIO support requires access to /dev/mem for reasons I haven’t dug into just yet.

OrangePi GPIO support currently requires a patch to Home Assistant, which you can find at a github branch.