Advertisement · 728 × 90
#
Hashtag
#housestuff
Advertisement · 728 × 90
Preview
Adding a Balcony Solar Kit To Our Capacity Our solar install is not far off three years old. We've got two east facing panel arrays providing 3.2kWp of PV capacity, supported by a `6kWh` battery. However, only having an easterly exposure has always been a _little_ bit of a bugbear. Although the panels work throughout the day, they start to wane around lunch time, wasting some of the potential that a sunny afternoon can bring. The house itself doesn't have a usable roof with a westerly (or southerly) exposure. The _garage_ , however, does. Although it obviously won't generate as much as the existing install, I decided to order a balcony solar kit for the garage, so that we could mop up some of the afternoon sun. This post describes the install process along with the initial results (despite the weather being a bit... British). * * * ### Balcony Solar In The UK These kits sometimes get referred to as "plug in" solar because, in Europe, they're widely available with a plug allowing them to be plugged into a spare socket. _Balkonkraftwerk_ kits are, apparently, hugely popular with renters in the German market. In the UK, however, it's **not** OK to simply plug into a socket and a dedicated circuit is required instead. It's not that plugging it in won't work, but that wiring regulations (and therefore home insurance) don't currently support doing so. However, that **is changing**: following a study, the government announced that it will legalise plug in solar, with an amendment to BS 7671 expected to land this year. This is, overall, a great outcome as it will make solar more readily available to renters. However, **my** system isn't connected via a plug: design and setup pre-dated the Government's announcement so I've had it hardwired in to comply with the existing regulations1. * * * ### The Kit My kit consisted of * 2 Trina 440w panels * A Hoymiles HMS-800w-2T 800w grid tied micro inverter * Various Fixings To suit my planned layout I also ordered a couple of extra bits * 2 pairs of DC Solar extension leads * A couple of pairs of hinged solar mounts * * * ### Install Topology Although I ordered the kit with the garage roof in mind, I was actually jumping the gun a bit. Our garage has an asbestos concrete roof, which we're planning on getting replaced this year. It's obviously not possible to mount panels on there without risk of disturbance, so I either needed to wait, or to install the panels into suitable temporary locations. I'm not _great_ at waiting, so I went for the second option. We've got a small lean-to shed attached to the end of the garage, and a pergola style walkway2 up the side, so I planned to place one panel on the shed and the other over the walkway: The microinverter would then sit inside the garage, protected from the elements. Although the shed sits a little lower than the garage roof (causing shade when the sun's to the south), it seemed like a good location to catch the waning sun and deliver electrons when we need them most: during peak pricing4 at the end of the day. * * * ### The Install With the challenges of working at rooftop height removed, installing panels isn't particular difficult. They **do** weigh a little more than you might expect, but are still within the bounds of a one-person job. To sit the panel at an angle and create a suitable mountpoint, I screwed a bit of wood across the top of the pergola and then used hinged mounts to fix the panel to it: So that the panel wouldn't flap in the wind, I also used hinged mounts to secure the other end to the cross beams: Before starting, I had thought that the shed mount would be the easiest of the two but, at install time, ran into an issue. So that I could make it tilt and face west, I'd ordered hinge mounts, assuming that they could be fitted along any edge of the panel. However, the panels only have fixing holes on the _long_ sides. Although it'd probably have been fine, I didn't fancy drilling new holes into a brand new panel, so decided to use flat mounts instead The downside of this is that it increases the likelihood of the panel being in shade. It's not a great _long term_ placement for it but, I hoped, would be OK for a while. DC cables run from the panels into the garage and back to the micro-inverter (which supports two strings, allowing me to track the two panels separately). The inverter's AC output is wired back to a fused isolation switch: To protect future sparkies, the warning sticker in our main fusebox has also been updated to list the garage as a generation location. * * * ### Initial Start-Up We had a _little_ bit of a false start. It was time for the grand switch on, so I connected the panels and flipped the isolation switch. The inverter's LED started blinking indicating that it was starting up. Because it can take up to an hour to sync with grid frequency, I walked away with the intention of checking back a little later. However, before that, I got a somewhat alarming message from my partner: I went downstairs and, as soon as I stepped out of the back door, was greeted with a very definite smell of burning. I went into the garage, flipped the isolator switch and then realised that it didn't smell inside, indicating that it was _probably_ coming from around the panels or their connectors. I checked the connections for each, then climbed up on a ladder to check the panels themselves. The shed panel didn't have any smell around it, the walkway one had more of a _hint_. The smell _was_ definitely dissipating though, so it was hard to ignore the coincidence of it coming and going around the time that things were turned on and off. I did a few extra checks * Pointed my thermal camera at _everything_ : no sign of anything being hot * Put my multimeter across the end of the DC extension leads - both panels were giving a good 37V, suggesting no issue with the leads or the panel end connectors I was **sure** that everything looked fine and yet, the smell... I had other things that I needed to be doing, so I decided to leave things disconnected until the next day, when I'd be more able to monitor it. Later that night, though, I was reading the local news and spotted this: We're far enough away that the smoke wasn't visible, but close enough (with the help of the wind) to have caught the smell. The next day, I connected things back up and, sure enough... no burning smell. * * * ### Monitoring Hoymiles inverters support both remote and local monitoring. Once I'd figured out getting the inverter onto our wifi it started reporting into the S-Miles Cloud: I haven't played around with it much, but it seems OK. * * * #### Connecting Telegraf I _already_ monitor our existing solar install so wanted to get metrics into InfluxDB. Happily, it turned out that someone has already created a telegraf plugin to connect to the inverter and retrieve metrics. I cloned the repo down, built the plugin and then moved the resulting binary to the directory that I tend to drop telegraf plugins into: git clone https://github.com/liwde/telegraf-hoymiles-wifi.git cd telegraf-hoymiles-wifi go build -o hoymiles ./cmd/hoymiles sudo mv hoymiles /usr/local/src/telegraf_plugins/ The plugin requires a separate config file to define inverters that it should connect to: cat << EOM > /usr/local/src/telegraf_plugins/hoymiles.conf [[inputs.hoymiles_wifi]] hostname = "192.168.13.227" EOM With everything in place, I configured `telegraf` by adding the following to `/etc/telegraf/telegraf.conf`: [[inputs.execd]] command = ["/usr/local/src/telegraf_plugins/hoymiles", "-config", "/usr/local/src/telegraf_plugins/hoymiles.conf"] signal = "none" I reloaded the telegraf service and points like the following started being written into InfluxDB: hoymiles_dtu,dtu_serial_number=xxxx dtu_energy_daily=52i,dtu_power=70.6 1774600109000000000 hoymiles_inverter,dtu_serial_number=xxxx,inverter_serial_number=xxxx inverter_power=70.6,inverter_temperature=13,inverter_voltage=246.4,inverter_frequency=50.03,inverter_current=0.28 1774600109000000000 hoymiles_pv,dtu_serial_number=xxxx,inverter_port_number=1,inverter_serial_number=xxxx pv_energy_daily=27i,pv_energy_total=84i,pv_voltage=31.5,pv_current=1.23,pv_power=38.8 1774600109000000000 hoymiles_pv,dtu_serial_number=xxxx,inverter_port_number=2,inverter_serial_number=xxxx pv_voltage=32.5,pv_current=1.09,pv_power=35.7,pv_energy_daily=25i,pv_energy_total=38i 1774600109000000000 * * * ##### Reporting Gaps One thing that I hadn't really thought about before, is that the inverter becomes unreachable overnight. It's powered by the panels themselves, so as the yield drops away the inverter goes offline: Happily, though, the telegraf plugin copes with this just fine. * * * #### Grafana As a start point, I added cells to my existing Grafana dashboard: I also created a new dashboard dedicated to the Hoymiles inverter: * * * #### Output Level Alerting Although it doesn't _really_ add much beyond some psychological safety, I created a Grafana alert to warn me if the inverter's AC output ever pushes towards its max capacity: The alert is driven by a simple query to get the maximum reported current in the queried period: SELECT max("inverter_current") FROM "Systemstats"."autogen"."hoymiles_inverter" WHERE $timeFilter The maximum output listed on the back of the inverter is 3.7A. However, 800W at 240v is around 3.3A so I decided to flag if it went much above that. * * * ### Limiting Export Levels In order to export electricity and receive payment for it, solar installs have to be registered with the local electricity distributor (the DNO). The idea behind this is that it allows the DNO to maintain an understanding of what might be fed back into to the local network. Following install of our main system, we received approval to export up to 3.6kW. I track our metered electricity usage via our glow IHD, so queried this data to check whether we've ever been anywhere near that level: from(bucket: "Systemstats") |> range(start: 1) |> filter(fn: (r) => r["_measurement"] == "smart_meter") |> filter(fn: (r) => r["_field"] == "export_now") |> aggregateWindow(every: 1d, fn: max, createEmpty: false) |> group() |> max() Our highest recorded export rate was `2.827kW`3. Although we've never hit our approved level, I still needed to make sure that the addition of the new panels wouldn't risk taking us past it. Because there's a data cable between our main install and the fuse box, our Solis inverter is able to read import and export rates from a meter which sits between our leccy supplier's meter and our consumer unit. The energy provided by the new panels feeds into the consumer unit side, so any export that it generates would be included in the Solis inverter's readings. I double checked that the Solis inverter was configured appropriately: As a fail-safe, I also added an alert to Grafana so that I'll be paged if our Smart Meter ever reports greater than 3600W of export: SELECT max("export_now") AS "export_now" FROM "Systemstats"."autogen"."smart_meter" WHERE $timeFilter GROUP BY time($__interval) FILL(null) I don't expect that this should _ever_ fire but, if for some reason our export does spike, I should find out in good time rather than relying on eventually being contacted by the DNO. * * * ### Performance Unfortunately, the install missed the recent spate of really sunny weather and most days have suffered from prolonged cloudy spells. However, as I'd hoped, the panels **do** help us produce electricity later into the day, taking advantage of the afternoon sun. However, the horizontal panel on the shed roof is outputting **much** less power than I'd been hoping for: That's partly a consequence of it being horizontal, but is also probably a time of year thing - it covers a part of the sky that the sun won't be high in until later in the year. Still, it's probably a sign that that location is a pretty poor use of a panel, so I'm considering relocating it to the walkway instead. What matters **most** , though, is that the panels are delivering energy some way into peak, supporting the battery and shaving some small amount off the power that we draw from grid as peak pricing starts. * * * ### Calculating Break-Even I've put quite a lot of effort into accurately tracking break-even of our main install, so the introduction of the new system presented a number of questions: * Should I track break-even for this system separately? * Should I update the existing calculations to factor in new system output (and cost) or only report separately? For now, I've decided to track it entirely separately - the kit cost hundreds, rather than thousands of pounds, so it didn't _quite_ seem worth risking (inevitable) mistakes updating existing calculations. Because there's no battery and no (separate) export, the calculations are quite a bit simpler, feeding into a new dashboard that's similar to my existing one: * * * #### The Downside Of Cheap Electricity It's a "nice" problem to have, of course, but being on Octopus Agile means that, on average, we pay less per unit: Break-even time calculations are based on the cost of the electricity that we'd have to have bought if solar didn't provide it. But, realistically, if we didn't have Solar (or more accurately, the battery) we _probably_ wouldn't actually be on Agile in the first place (and certainly wouldn't achieve such a low average unit price). To get an idea of the difference, we need to look at Fixed price tariffs instead: * British Gas: 25.817 p/kWh5 * Octopus: 25.86 p/kWh * EDF: 27.69 p/kWh Splitting the difference between those prices gives a unit cost of 26.73 p/kWh6. Although only a matter of pence more, it's still a 40.7% increase: if I used fixed rates in the calculations, the time to break-even would almost halve! With that in mind, the current projected break-even doesn't look quite so bad, especially given that the figures are currently based on a single cloudy week. * * * ### Conclusion Although the balcony solar kit only provides 25% of the capacity provided by our existing install, the capital cost was **significantly** lower (helped, of course, by not needing scaffolding). The break-even point should, therefore come quite a bit sooner. Once the garage roof has been replaced, the panels will move to a location where they'll be better exposed to the sun (and for longer). The fact that they'll be most active during times of peak pricing should further accelerate them down the path to payback. Time will tell, but I wouldn't be overly surprised if the break-even period ends up being just a few years (which will be all the more likely for others if these kits ultimately end up in the Aldi middle aisle). * * * 1. This isn't a bad thing really, even if the timing is a _little_ annoying ↩ 2. Which I've always hated anyway ↩ 3. Which, rather than being panel driven, is almost certainly from me telling the battery to dump to grid during a savings session or similar ↩ 4. Essentially, this panel's role will be to support the battery in supplying our peak usage. The battery already doesn't make economic sense so adding capacity to it isn't really an option. ↩ 5. In keeping with Centrica's awful customer service, you need to provide usage details to see tariff prices. Which is only part of why friends don't let friends use British Gas. ↩ 6. Though, between writing this and publication, prices are expected to come down a bit as a result of Ofgem lowering the pricing cap ↩

New #Blog: Adding a Balcony Solar Kit To Our Capacity
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/a...

#electrical #housestuff #solar #telegraf

0 0 0 0
Two 4way multifunctional utility key and sillcock set

Two 4way multifunctional utility key and sillcock set

Latest purchase for planning ahead for the worst, hoping for the best. Do you have one? If so, have you used it? #planning #prepping #housestuff

0 0 0 0
Preview
Replacing a Google Nest Thermostat with Home Assistant Not long after we moved into this house, we installed a "smart" thermostat: a Google Nest Gen3. I've disliked it ever since. Lowlights include "True Radiant" turning the boiler on 5 hours early and Home/Away assist turning the heating off while we watched a movie (we hadn't moved from the sofa, so _obviously_ it decided that no-one was home). Once I'd turned off all of its "smarts", my dislike for the thermostat simmered down to something closer to disdain: what we now had was a massively overpriced schedulable thermostat. Except, it was actually slightly _worse_ : normal thermostats don't come with a dependency on Google's cloud platform (a dependency that only increased once I hooked it up to Home Assistant so that I could restore some level of smartness). Google recently end-of-lifed the Gen 1 and Gen 2 Nest thermostats leaving owners with a dumb (but at least working) thermostat. This served as the kick up the arse that I needed: the writing would probably soon be on the wall for my Gen 3 too and, if I wanted to maintain my current capabilities, I'd need to find _something_ to replace it. Four years ago, I wrote the following: > Given the choice between NEST and Hive, I think I'd now go HIVE, but realistically the next time I'm in the market, my choice will probably be "neither". Although I did (briefly) look at Hive, I decided that it was best to stick with my original instinct and look for local-only hardware. This post describes replacing our Nest thermostat with locally controllable hardware, hooked up to Home Assistant for scheduling, control and automation. * * * ### Contents * Contents * Topology * Hardware * Boiler Control Thermostat * Room Thermostat * Installation * Matter Server * Room Thermostat * Boiler Control Thermostat * Matter Pairing * Helpers * Automation * Linking The Devices * Scheduled Temperature Changes * Disaster Recovery Plan * Graphs * Matter Over Wifi * Conclusion * * * ### Topology The layout of our house made this a little more difficult than it might otherwise have been. The boiler lives in an unheated single story extension on the back of the house, while the thermostat lives in the middle. The Nest thermostat connected wirelessly to the boiler via the Heat Link. Because the extension is unheated (and somewhat under insulated), the thermostat needs to be in the house. However, my investment in this project didn't really extend to pulling up floors and running wires, so the replacement thermostat would also need to be wireless. * * * ### Hardware I searched around for a _good_ wireless thermostat and control box, but didn't really find much - especially given that I needed something which was local only and automatable. Eventually, I concluded that I wasn't going to be able to get exactly what I needed and so would need to put multiple products together. * * * #### Boiler Control Thermostat After a lot of searching around, I settled on a thermostat made by Meross, the MTS215B: Although I'd originally hoped to find a Zigbee thermostat, the Meross had the best reviews and was a reasonable price. It'd be my first Matter device, but I wasn't _too_ concerned about that1. * * * #### Room Thermostat I did, briefly, consider using a simple and cheap zigbee temperature sensor: it could stick unobtrusively to the wall in the hall and report back the temperature in the most central part of the house. However, that wouldn't be a like-for-like replacement for the Nest thermostat. I decided that I wanted something that would allow us to adjust the target temperature level without having to whip out a phone. That requirement narrowed the market quite considerably, only really leaving the Ecobay Master Controller: This zigbee temperature control unit seems to have been created with radiator TRVs in mind: you join them all to the Tuya app and then group them. But, crucially, it had all of the features that I needed: temperature monitoring, target temperature input and was wireless (though, disappointingly it turned out to be battery only - I'd assumed the slot on the side was for a USB power supply). Tuya kit can be a _little_ funny, so there wasn't any guarantee that it'd be able to work with Home Assistant, but for £25 it seemed worth a go. * * * ### Installation #### Matter Server Not previously having had any Matter devices, I needed a Matter server for Home Assistant to communicate with. So, whilst waiting for the thermostats to turn up, I stood up a python-matter-server container: mkdir -p docker_files/matter/data docker run -d \ --name matter-server \ --restart=unless-stopped \ --security-opt apparmor=unconfined \ -v /home/ben/docker_files/matter/data:/data \ --network=host \ ghcr.io/matter-js/python-matter-server:8.1.1 (I don't particularly like running containers in host networking mode, but apparently it's necessary in this case). With this up and running, I logged into Home Assistant and went `Settings` -> `Devices and Integrations` -> `Add Integration` -> `Matter` When prompted, I provided the address that the Matter server was listening on: With that, it was done. * * * #### Room Thermostat The temperature unit was quite easy: install batteries and join it to the Zigbee network. That said, I did briefly think that I'd wasted my money - zigbee2mqtt reported that the device was unsupported and did not display any readings from it. However, my installation was quite old - I updated it and the device was then correctly recognised: The device appeared in Home Assistant as a `climate` device: It also exposed a `boolean_sensor` to indicate whether it was currently calling for heat: * * * #### Boiler Control Thermostat The Meross thermostat was, understandably, a bit more complex to install. To begin with, I turned off power to the boiler. There was a small captive screw on the underside of the heat link, I slackened that off and removed the cover: Rather unhelpfully, the terminals were only labelled with numbers. So, to find out which wire was which, I had to find the Nest install doc: So, in the photo above * Terminal 2 (brown) is common * Terminal 3 (blue) is call for heat (or "normally open"/NO) I screwed a new (deep) pattress to the wall, passed the wires through and then screwed them into the back of the meross thermostat (the diagram is offset, I'm using L, N, NO and COM): I screwed the thermostat into the back box and then clipped its screen back on. After restoring power, the thermostat came to life I turned the temperature up to make sure that the boiler kicked in, then turned it back down to make sure that it shut off. * * * ##### Matter Pairing With the thermostat working, the next step was to connect it to the Matter network (known as a Fabric). Part of the setup process uses bluetooth: Home Assistant relies on the mobile app to handle this. I got my phone out of my pocket and, to my surprise, it was already prompting me to add the newly detected Matter device. After hitting `continue`, I was prompted to scan the device's QR code. Android then asked me if I wanted to add the device to Google Home. There wasn't a "fuck no" button, so I had to settle for "use another service instead" - this displayed a couple of options, including Home Assistant. Moments later, the thermostat was enrolled into and could be controlled by Home Assistant, again appearing as a `climate` device: * * * #### Helpers I don't like hardcoding values into automations (I tend to forget where to look), so I created a set of helpers in Home Assistant to store important values * `boiler_control_on_temp`: the temperature to set the thermostat to when we want the boiler on (set to 29) * `boiler_control_off_temp`: the temperature to set the thermostat to when we want the boiler off (set to 10) * `heating-low`: the temperature to set to during "low" periods * `heating-high`: the temperature to set to during "high" periods To allow for easy tweaking in future, I added these to my Heating dashboard * * * ### Automation Both of the devices now existed in Home Assistant but weren't, in any way, linked. Leaving them like this would have been problematic - the extension that the boiler lives in is unheated, so the boiler control thermostat would have had a _completely_ unrealistic view of the house's temperature (likely bankrupting us in its attempts to sufficiently warm an unheated cupboard). * * * #### Linking The Devices The Meross thermostat doesn't have a simple on/off toggle, so to control the boiler's state I needed to set it to a temperature which would cause it to start (or stop) the boiler. The room stat would push readings into Home Assistant which could then distil the reading down to a binary choice (on or off) and then set the boiler control thermostat to the corresponding temperature: Originally, my plan had been to create a `Generic Thermostat` and link both ends to it. However, I ran into some issues with the way that the Zigbee panel appeared in Home Assistant and was unable to get it to feed its temperature readings into the generic stat. This wasn't a major issue, though, because the panel also exposes a boolean sensor to indicate whether it's calling for heat. When I ordered it, I'd expected a simple input/output and had assumed that the thermostat logic was within Tuya. But, the device itself is actually a thermostat and so is able to tell Home Assistant when it'd like the boiler to turn on. I created an automation to turn the boiler on when the panel calls for heat: alias: Turn Boiler On description: "" triggers: - trigger: state entity_id: - binary_sensor.temperature_panel_heating_demand to: - "on" conditions: [] actions: - action: climate.turn_on metadata: {} data: {} target: entity_id: climate.smart_thermostat - action: climate.set_temperature metadata: {} data: temperature: "{{ float(states('input_number.boiler_control_on_temp')) }}" target: entity_id: climate.smart_thermostat mode: single I created a second automation to handle turning the boiler back off: alias: Turn Boiler Off description: "" triggers: - trigger: state entity_id: - binary_sensor.temperature_panel_heating_demand to: - "off" conditions: [] actions: - action: climate.set_temperature metadata: {} data: temperature: "{{ float(states('input_number.boiler_control_off_temp')) }}" target: entity_id: climate.smart_thermostat mode: single Technically these could have been combined into a single automation using an `if`, but having them separate makes it easier for me to see when each state last fired. I turned the target temperature up on the Zigbee panel and, sure enough, the boiler kicked in. * * * #### Scheduled Temperature Changes Nest **do** have quite a nice scheduling interface: This schedule is a big part of how we reduced our heating bills: I'm at home during the day, but I'm not as prone to feeling cold (**and** I'm sat in a particularly warm room anyway). Ideally, I wanted to be able to maintain a similar level of control. Unfortunately, Home Assistant's scheduling functionality isn't quite as polished. The `Schedule` helper _does_ allow you to provide arbitrary data (which could be used to defined the desired temperature) in scheduled blocks, but there isn't a way to have that data display as nicely as in Nest. Some of the temperatures that I'd scheduled in Nest were very similar though, so I decided to try shifting to a two-tier schedule * Low: 15C * High: 19C (I rounded values down because I'd rather be slightly too cool than slightly too warm) I stored these values in the number helpers `heating-low` and `heating-high` and then created a `Schedule` helper to control when we transition between each: `on` periods are `High` and `off` is `Low`: I then created a couple of automations to respond to changes in the schedule state by setting the zigbee panel's target temperature to the appropriate value: alias: Switch Heating To High description: "" triggers: - trigger: state entity_id: - schedule.heating_high_schedule to: - "on" conditions: [] actions: - action: climate.set_temperature metadata: {} data: hvac_mode: heat temperature: "{{ float(states('input_number.heating_high')) }}" target: entity_id: climate.temperature_panel - action: climate.turn_on metadata: {} data: {} target: entity_id: climate.temperature_panel mode: single --- alias: "Turn heating down" description: "" mode: single triggers: - trigger: state entity_id: - schedule.heating_high_schedule to: - "off" conditions: [] actions: - action: climate.set_temperature metadata: {} data: hvac_mode: heat temperature: "{{ float(states('input_number.heating_low')) }}" target: entity_id: climate.temperature_panel - action: climate.turn_on metadata: {} data: {} target: entity_id: climate.temperature_panel Setting these automations live meant that I'd reached feature parity with the Nest that I'd replaced. Finally, I worked through my existing automations (including the overnight boost script) and updated them to point to the new `climate` device instead of the one associated with the Nest thermostat: * * * ### Disaster Recovery Plan Although the Zigbee temperature panel is a nice bit of kit, I did have some reservations about the setup being _quite_ so tightly tied to it: zigbee devices don't always have the best build quality and, if it were to die, we could be left without working heating. So, I decided to define a DR procedure in the event that the panel failed: * Create a boolean helper to act as the heater switch * Create a new `Generic Thermostat` * Bind the thermostat to some other temperature sensor (probably the one in the living room) * Update automation to turn boiler on/off when the new boolean helper changes Unfortunately, there isn't quite so much that could be done if the Meross thermostat were to die - I'd probably have to reconnect the Nest while I waited for a replacement to arrive. Losing Home Assistant would also be pretty bad (though not just because of the heating). However the Meross also supports use of an external temperature probe. It seems quite likely that there's a preexisting cable run from the boiler to somewhere in the main house (there _must_ have been a thermostat at some point). The next time that I've got flooring up, I'll try to find it so that I can install a temperature probe in the house, giving the Meross the ability to run entirely on it's own if required. * * * ### Graphs I have Home Assistant set up to write metrics into InfluxDB and so already had relevant Grafana dashboards. Because the Zigbee panel exposes a binary sensor, it was easy to write a query to display its reported heating state: from(bucket: "home_assistant/autogen") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == "binary_sensor.temperature_panel_heating_demand") |> filter(fn: (r) => r._field == "value") |> aggregateWindow(every: v.windowPeriod, fn: max) To allow comparison, I added this query to a cell which shows Nest's reported status: The new thermostat is a bit more prone to flapping than the Nest, turning on and off in quite short time periods. Unfortunately, the device doesn't have a setting to control this (if I'd been able to use a `Generic Thermostat`, I could have set `cold_tolerance` to address this). I'm not sure that it's too big of an issue, but if it continues to bug me I can build a delay in to debounce the automations. I also added a series to my temperatures graph. It seems that the zigbee panel tends to report a lower temperature than Nest (the initial spike is because the panel was sat in my office): This was a useful data point as it indicated that I should probably _reduce_ the target temperature to account for the panels (apparent) under-read2 * * * ### Matter Over Wifi Although I'm impressed with the ease of integration that Matter brings, I _do_ have some initial concerns with it * It's an IP capable device: I more or less have to trust that the firmware isn't configured to do unwanted stuff * Devices need to be in the same network as Home Assistant: Matter uses link-local IPv6 addresses and IPv6 Multicast, so I can't isolate devices onto my dedicated IoT wifi network (at least, not without multi-homing Home Assistant onto it) But, in practice, these concerns aren't all that different to those I have about things like the Kasa/Tapo wifi smart sockets. Essentially, I've been spoilt by Zigbee. * * * ### Conclusion This project seems to have been a success: Automation of our heating is no longer reliant on the reachability of GCP's pub/sub system and our thermostat is no longer reporting into Google. Although the hardware is a little more piece-meal than I'd originally planned for it to be, our heating is now _entirely_ locally controlled (though the Home Assistant mobile app means that I _can_ still control it when out and about). Because we were originally using Home Assistant for other things, it's only _slightly_ more load bearing than it had been before3. The boiler control thermostat is also my first Matter device: so far I've been pleasantly surprised with the ease of integration. I do recall seeing quite a few complaints about Matter, but I _think_ that was often more in the context of Matter-over-Thread than about Matter itself. Either way, the Nest is _finally_ gone. * * * 1. There **are** some caveats with using Matter though. The biggest is that you will need to ensure your LAN supports IPv6 (you don't need external support, but you do need to not have turned it off everywhere that you could find) ↩ 2. I haven't yet checked whether it's the panel or Nest that's reading incorrectly - it doesn't _overly_ matter as long as I know what _relative_ value to set targets to. ↩ 3. Though the change is enough that I've put a task on my list to move it to dedicated hardware and think more about redundancy ↩

Replacing a Google Nest Thermostat with Home Assistant
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/r...

#heating #homeassistant #homeautomation #housestuff #matter #nest

0 1 0 0
Preview
Building An Air Rifle Target Back-Stop I've been getting back into shooting recently. There's a fantastic outdoor centre just up the road, boasting a number of woodland ranges. But, although trips there are good fun, it's not exactly the most cost-effective way to shoot an air-rifle. We're pretty fortunate in that we have quite a long and wide garden so I decided to build a small backstop to allow me toshoot safely in the garden. This post describes the process of building a semi-portable backstop for air-rifle shooting. Note: the UK legal power limit for an unlicensed air rifle is 12ft/lbs - if you're elsewhere, with something like a 26ft/lbs rifle you _might_ want to double up a bit. * * * ### Pellet Catcher vs Backstop First, let's be specific about what it is that I'm building. A pellet catcher (or pellet trap) sits behind the target but **only** the target: it'll stop pellets that hit the target card, but not any that are off (mistakes can and do happen). I've got a couple, which look like this: These traps provide a convenient way to hold the target card **and** stop the pellet afterwards. However, they don't provide any security in situations where the pellet has gone wide. Just as importantly, these pellet traps do nothing to protect the target card from rain - despite having a sheltered area to shoot _from_ I can only really use them when the British weather deigns to allow it1. A backstop is wider than the target and can also be used with targets that aren't so easily mounted on a small metal box (like spinners). Although a backstop doesn't **have** to provide protection against the weather, mine is going to. * * * ### The Design I wanted a small wooden unit that targets could sit inside so that, when not in use, we'd have a small unobtrusive wooden cabinet in the garden. After a bit of browsing around, I settled on one of these: I selected this unit based on a number of criteria * Price: I'm likely to put holes in it, so I _really_ didn't want to spend a fortune * I couldn't go _too_ cheap: cheaper items tend to be made of chipboard which doesn't do well in the wet * It's made of Fir, which is pretty environmentally sustainable * It's got a strip of roofing felt on top to help keep the contents dry The last one might sound a bit "duh", but there was another fir unit at around the same price with a flat slatted roof - the reviews were full of people complaining about it not being waterproof. * * * #### Stopping Pellets No cabinet on the market, on it's own, is going to reliably stop air rifle pellets (particularly over time), so the plan was to build a new multi-layered back into the unit. That back would be mounted at an angle behind heavy curtain strips, with the middle shelf left in to create two compartments: The false back is mounted at an angle for a couple of reasons: * it helps to redirect any ricochets downwards * for pellets which do penetrate it, the distance needed to pass all the way through is increased The backboard is made up of 4 layers: The pellet first hits the rubber, before passing in the cork. Although the rubber is tough, both layers compress easily, sapping some of the pellet's energy. If the pellet manages to pass through those, it then reaches the much harder 9mm plywood. A sheet of steel could achieve a similar effect, but the aim of the backboard isn't _just_ to stop the pellet, it's designed to do so as quietly as possible: I have neighbours, so flinging lumps of lead at a noisy backboard is likely to be unwelcome. Because the backboard is mounted at an angle, there's space to add more material behind it to ensure that any pellets that make it through the plywood do not exit through the back of the cabinet. * * * ### The Build First things first, I built the cabinet (which came as a flatpack): I left the shelf out in order to see whether it would be feasible to have a single backboard running the height of the cabinet - I ultimately decided that it was a little too shallow for that and re-fitted the shelf. I drilled a couple of holes on either side of the unit and screwed a bit of timber across the top. To help reduce the noise made by vibrations I stuck (and then stapled) some adhesive backed foam across the top and back of it: I ran another piece of foam across the bottom of the compartment too, so that the backboard would only ever make contact with foam. It was time to make the backboard itself. I took a piece of 9mm plywood and cut it down to size (annoyingly, the damp seems to have got to my wood-stock) Having sanded the mould off, I sprayed the surface with spray glue and then stuck down a layer of cork tiles: Then, I added a second layer, shifting the tiles up slightly to help make sure that the tiles met in different places within each layer. Finally, I wrapped the whole thing in rubber flooring The rubber flooring comes on a big roll and is only about 3mm thick. Had it been much thicker, though, I'd have struggled to get it to wrap around the edges of the board. * * * #### First Test I was _fairly_ sure that my design was sound, but I wanted to be certain: I didn't want to waste my time finishing the build only to find that the backboard didn't stop pellets as well as intended. So, I slotted the backboard into place and put out two of the pellets that I commonly use: one spiked, the other a diablo: For the first test, I used my air pistol. The manufacturer _claims_ that it sits just below the UK legal limit for air pistols (6 ft/lb). However, my (admittedly cheap) chronograph suggests that it's delivering more like 4.5 ft/lbs. From 5 metres away, I shot the backboard with the diablo. I didn't penetrate the rubber and instead left a little impression of itself This wasn't necessarily a good thing: although the backstop went undamaged, the pellet had _bounced_ (albeit in this case downwards). Next, I loaded the spiked pellet, which also left a little impression in the rubber. I dispensed two more pellets and set the air-rifle up at the 5 metre mark. This is **much** closer than I'd ever normally shoot, but the aim was to ensure that the backstop did actually trap and stop the pellets. UK law allows air rifles to have more power than pistols. Again, the manufacturer claims that it's just under the limit (12 ft/lbs3), but my chronograph puts it at more like 10.5ft/lbs. Both pellets went through the rubber and stopped inside the cork layer * * * #### Making a Curtain Happy that the backboard was able to effectively trap pellets, the next step was to make a curtain to sit in front of it. I'm sure that this isn't the _actual_ name for it, but I know the style as a butcher's curtain (I guess that's probably where I first saw one): a curtain made up of thin strips that you walk straight through. The curtain serves two main purposes. Firstly, it helps trap rebounds: we want to ensure that bouncing pellets do not find their way out of the cabinet because they may ultimately end up hurting somebody. Secondly, it steals energy from pellets as they come in. They're not capable of stopping a pellet on their own, but as the pellet passes through it, the curtain strands will move, converting some of the pellet's inertia into curtain movement. To make the curtain, I ordered a cheap rubber doormat: The fabric attached to it is intended for wiping boots, but helps by adding weight. I turned the mat length-ways and cut strips into it, before stapling it along the piece of wood at the top of the unit. As an idea of scale, the pellet trap in this photo holds a 17x17cm target card: * * * ### Testing It was time to give the unit a proper test and see whether any pellets escaped through the backboard into the gap at the back. Obviously, there's not a **huge** amount of value in testing by shooting at a metal pellet trap, so I stood a wood block with some spinners on the shelf too, and then shot from 20 metres away. I used both the rifle and the pistol and shot _a lot_. When I checked it, the backboard was pretty well peppered The photo shows a number of marks where pellets have bounced rather than penetrating. The pile of spent pellets at the bottom suggests that the curtain did it's job. But... did any pellets make it into the back? One did, although it seemed to have taken an unusual route: There was no visible hole in the backboard's plywood. However, on the right hand side, there's a chip in one of the wood panels which make up the cabinet's side. The pellet in question appears to have managed to squeeze between the backboard and the side. That should be a pretty rare occurrence, though, and the cabinet's walls are thick enough that a pellet's unlikely to make it through them that way. The curtain, for it's part, was riddled with holes but holding together just fine * * * ### Conclusion The backboard won't last forever (nor is it designed to), but I now have a cabinet which can be used to provide an extra layer of safety should a shot veer off course (or, of course, be aimed at a backless target like the spinners). Our garden isn't nearly as tranquil2 and idyllic as the woodland range down the road: But, it _is_ a lot closer and cheaper to use. Currently, only the top of the cabinet has a backstop installed. My intention _is_ to build one into the lower half, but I thought it best to wait and see how I get on with this build before duplicating it: the curtain already sits a little close to the front for the spinners, so it **might** be that I try a different design for the bottom. The only thing that's sure, is that it'll probably involve rubber flooring: I've still got more than half a roll left! * * * 1. In fairness, so far this year the weather seems to have been focusing on trying to melt rather than drown us ↩ 2. OK, maybe tranquil isn't quite the right word: it's not exactly _quiet_ with shotguns going off and bits of clay dropping out of the sky. But it's a _different type_ of background noise and no-one is going to interrupt you. ↩ 3. Air rifles with higher power output can be obtained with a firearms license, but then you end up with all of the drawbacks of shooting rimfire but without the benefit of the cordite smell. ↩

Building An Air Rifle Target Back-Stop
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/b...

#building #diy #housestuff #shooting

0 0 0 0

What are your moving and packing tips, please?

#tips #moving #housestuff #movinghouse

0 0 2 0

Spent 2 hours on the phone with the house insurance company and only got some of my questions / requests handled. I’ll do the rest next week. 🤞

Also, finally went to the shopfront to complain about my crappy induction stovetop. There will be some action, thank goodness. #HouseStuff

0 0 0 0
Preview
Bonnie's Lap Spay Diary Bonnie came to live with us at the beginning of last year. She joined our family unspayed and so was still having regular seasons. I don't _think_ that her previous owner had tried to breed her but, as he had a breeder in the family, I'm guessing that it was an option that he wanted to keep. Intact dogs aren't just exposed to the risk of accidental pregnancy, they also have an increased incidence of cancer and Pyometra (on average, 19% of intact females will suffer this before age 10). On top of that, of course, is the disruption to routine that having a season brings: although the dog will still want to play, there's an obvious need to keep them away from males (intact or otherwise: neutered males can still end up fighting over a hormonal bitch). Getting Bonnie spayed was the _responsible_ thing to do. However, Bonnie joined us with some behavioural issues and the early relationship between her and Pepper was sometimes a little shakey. We felt that their developing relationship might be negatively impacted by a surgery recovery period. Over the last year or so, though, Bonnie has made great progress and we decided that the time had come to have her spayed. In order to supervise the dogs while Bonnie recovered, I took some time off work and, frankly, got quite bored and restless, so thought I'd write about her recovery. * * * #### Visiting The Vet When Bonnie first joined us, she suffered quite badly from anxiety, particularly whilst on a lead. Although it's improved considerably, it's very much still a thing. It should come as no surprise then, that she **does not** enjoy visits to the vet. Bonnie's first visit to her new vet was for a vaccination booster and, although she received the jab, it was clearly quite a stressful experience for her (and everyone else!). After that, the vet provided a small supply of anti-anxiety medication (Gabapentin and Trazodone) for me to give to Bonnie ahead of any visit. The next visit to the vet was unplanned: Bonnie had caught her nail and torn it most of the way off. I gave her some tablets a few hours before the appointment and, once we were in, she _even_ let the vet stroke under her chin. The tablets _definitely_ work, but they're not a panacea: they buy a little bit of time for the vet to do what needs to be done, but Bonnie can still eventually get worked up. * * * #### The Morning Of The Procedure In order to minimise stress for Bonnie, the plan was simple: * Dose her with anti-anxiety meds the night before (and morning of) the procedure * Take her into the vets before anyone else might arrive * Do a quick bit of paperwork and then sedate her Just before the sedative was injected, for the vet's safety, I put a plastic muzzle over Bonnie's snout. This was quite new to her, but she let me put it on and take it off without any fuss - it turned out not to be needed, she didn't even growl when the needle went in. Having injected the sedative, the vet and nurse left the room to allow Bonnie to settle. Within about 15 minutes of arriving, Bonnie was curled up unconscious at my feet: The nurse came back in and, after taking a list of additional requests (trim her nails etc), carefully picked Bonnie up and carried her through. I went home, made a cup of tea and then washed her bedding to make sure she'd that have a clean bed to come home to. * * * #### Laparoscopic Procedure We opted for a laparoscopic spay1. A traditional spaying (like the one Pepper had) involves a long incision down the abdomen, giving the vet access to their ovaries and uterus. A laparoscopic procedure, instead, involves a couple of small incisions. The vet pushes a camera through one to help them see (hence "keyhole" surgery) whilst operating through the other. The procedures are also quite different in terms of the impact that they have on the dog's body. Lap spays only remove the ovaries (an _ovariectomy_), whilst traditional spays tend to remove the uterus as well (an _ovariohysterectomy_), inevitably disturbing more of the remaining tissue in the process. For those don't mind a bit of inside imagery, there are videos of Lap Spays on Youtube. Key-hole procedures are more expensive2, but bring a real advantage in terms of recovery time: | Traditional | Laparoscopic ---|---|--- **Recovery Time** | 14 days | 3-4 days The crucial difference is that Bonnie was allowed to run and play again after just a few days (although she'd need to be kept from licking the wound for 10 days). This is a **huge** improvement over the two weeks that Pepper spent on lead and wearing a t-shirt (the vets couldn't get her to stay in a cone, even before we arrived): I dropped Bonnie off at the vets at 08:40. By 11:30 they had phoned me to say that everything had gone well and that she was awake enough to be collected. * * * #### Coming Home I wasn't sure how awake "awake" was actually going to be. Bonnie had been sedated during the nail-tear visit and, that time, "awake" had meant an eyes-wide, tongue hanging, spaced out dog who needed to be carried because she couldn't walk more than a few steps. This time, though, Bonnie trotted into the waiting room wearing a snazzy pink recovery suit: The suit proved to be pretty good: rather than having a buttoned up flap that I'd need to undo whenever she needed the toilet, it was elasticated and so stretched out of the way whenever she squatted. * * * #### Day 0: Dopey Dope Dope Although able to walk on her own, Bonnie was quite clearly still feeling the effects of the methadone that she'd been sedated with. She was slow to respond, easily confused and had pupils like spaceships: Although it was one that I had been prepared for, her lack of re-activeness did pose something of a problem. The last time that Bonnie came home doped up, Pepper was _beside herself_. She couldn't seem to understand why her buddy wasn't responding to her and responded by getting really close and barking anxiously at her 3. This time, although Bonnie was mobile, she still wasn't particularly responsive and Pepper started worrying again. To allow Bonnie to recover in peace, we took Pepper up to littlun's room and left her to ~~be entertained~~ watch littlun play on the Xbox for the evening. Around 1930, we brought Pepper back down and, keeping her on a lead, I got both dogs to settle down next to me and go to sleep. A few hours later, I moved them to their crates and said goodnight. * * * #### Day 1: Hooman, PLAY The next morning, I got up and made their breakfast, squirting a pre-prepared dose of Rheumacam onto Bonnies. When I went through to let them out of their crates, it was clear that Bonnie had slept the remainder of the sedation off - if she hadn't been wearing a bright pink reminder, you could _easily_ have forgotten that she'd had surgery at all. After feeding them, I clipped their leads on and took them for a short walk. During that walk, Bon had a wee and a poo - both are something that you're supposed to get excited about post-sedation4. In preparation for having a post-operative dog in the house, I had put a step-stool in front of the sofa (and another by our bed). Unfortunately, telling a Border Collie not to jump is a bit like telling a toddler not to eat sweet things. Bonnie ignored the step and jumped straight up... _sigh_. The _real_ challenge, though, lay in keeping the two dogs from playing. _This_ was the reason that I needed to take time off work: both dogs _love_ a game of bitey-face and my primary role was to pour a (metaphorical) jug of cold water on any sign of play. Using a precise combination of exasperation, darkened rooms and the words "settle down", we got through the day without any rough play. * * * #### Day 2: Grrrr Day 2's morning walk didn't end as well as the previous one. Normally, once we get back to the house, the dogs sit and receive a small treat (which is used to reinforce the command "come home" in case they ever do get out of the garden). But, after swallowing hers, Bon nipped jealously at Pepper's muzzle. I've written before about Bonnie food guarding, but it's _very_ rare for it to show up when they're both receiving a treat (particularly one that both dogs can finish quickly). There's no way to be sure, but I _think_ the issue was that Bonnie was in some discomfort and felt less tolerant and reasonable than normal: her painkillers were administered once daily, so it's quite likely that the previous day's dose had worn off and the next hadn't kicked in yet. It's equally possible, though, that it was temperature induced grumpiness. The UK was going through (yet) another heatwave, which wasn't exactly ideal timing for a double-coated collie to be wearing an outer body suit. Whatever the cause, with a quick verbal correction, Bonnie went back to being nice and the day continued. However, Bonnie was also fully back to her old self and jumped5 on every possible opportunity to release some of her immense Collie energy. That included trying to chase pigeons in the garden, running up and down the stairs and, of course, finding ball-shaped things to drop in my lap in an attempt to try and tempt me to throw them: * * * #### Day 3: Check the wound... but how? It was time to check the wound. With a less anxious dog, the vet arranges a post-operative appointment to check that the wound is healing and isn't showing any signs of infection. Those appointments can be _extremely_ stressful for an anxious dog, though, so they asked me to email photos instead. Day 3 was _the_ day, but the question of **how** had been running through my mind _for days_. In order to even _see_ the wound, we'd need to do the following: Bonnie seems to trust us, but it's very much on her terms. The chances of success seemed _incredibly_ remote and brought some associated risks: * If the suit came all the way off of her hindquarters, there was no chance we'd be able to get it back on * If she stressed or wriggled too much, she could end up tearing the stitches and re-opening the wound * If she got too stressed, she might bite (crossing a line that she hasn't yet) This was complicated further by the fact that it would need to be _me_ holding her head: much to my family's annoyance, she has the best relationship with me and so was less likely to overreact or _hold a grudge_. Even with the difficulties, though, the alternative was to leave her health to chance, so we **had** to try. * * * ##### Checking The Wound I knelt next to Bonnie, holding her lead short (but not tight). I pressed the flat of my hand against her cheek, sandwiching her head lightly between my palm and my body, effectively holding her in place. The _moment_ that the suit's zip was touched, she growled and tried to flick her head round. Talking soothingly, I tightened my grip a little and nodded to continue. With the zip undone half-way up her back, the back of the suit was pulled gently down her legs. It became clear that there was problem: although a shaved patch of her underside was visible, it wasn't really possible to see her wound and certainly wasn't going to be possible to get a _useful_ photo. I carefully released her head and lent down to have a look myself - there really was just no way that it was going to work: * The suit was elasticated and tight, so we couldn't move it to increase visibility without taking it off her legs (at which point, it'd never go back on) * If we poked a phone into the suit and she wriggled, we'd likely end up rubbing whatever bacteria was on the phone over her wound, potentially introducing an infection that hadn't been present before With my head at an uncomfortable angle, I _thought_ I could occasionally see half a glimpse as she shifted, but she was starting to get quite agitated - we were out of time. I held her head again and we zipped the suit safely back into place before releasing her and rewarding with a treat. * * * ##### Reporting Back I emailed the vet, noted that I hadn't been able to get a picture and provided the evidence that I _had_ been able to collect: * The shaved area was all a normal colour * There was no obvious sign of swelling * There was no malodour * She was mobile and seemed her usual self * There was no evidence of discharge or dampness on the suit itself * Apart from that first day, Pepper had not been sniffing around her Being _a little bit_ of a nerd, I also pointed my thermal imaging camera6 at Bonnie's belly (and, yes, I forgot to set the clock the last time that the batteries died): I'm more than a little out of my lane here but, if there were any infection, I would expect the associated vasodilation to cause a hot/warm spot around the wounds. Although the numerical values can't be taken literally they _were_ about the same as when I pointed it at Pepper's belly (suggesting that Bonnie wasn't feverish). The vet _didn't_ comment on my (potentially) questionable application of science, but agreed that it sounded like things were healing as they should. * * * #### Day 4: Play? Play Day 4 was the point at which Bonnie _could_ be allowed to be more active. However, as we hadn't been able to _physically_ check the wound, I decided that it would be wise to wait a little longer so that we could be reasonably sure that she wouldn't tear it (particularly as there was no way to know whether her earlier pigeon chasing and sofa jumping antics had already done so). Obviously, Bonnie had no way to know that day 4 was _supposed_ to be the day and so also wouldn't _really_ know that the recovery period had been extended. Unfortunately, neither did she know _why_ she wasn't allowed to play or even really _that_ she wasn't supposed to. Inevitably, our lunch time spell out side included more attempts to make me acknowledge the presence of a ball: * * * #### Day 5: Extra Time After five days of me disrupting their play and insisting on calm, the dogs tried a new tactic: one dropped a toy into my lap just before the other launched at my face to kick off a human-dog game of bitey-face. In life, there are moments when it's impossible to be strict and one of those is when your dogs seemingly collaborate to launch a sneak attack on you. A three way game of bitey-face ensued, before one dog decided that I needed the handicap of playing tug-of-war whilst still playing bitey-face with the other. Nearly a week's worth of pent up energy was essentially unleashed on me by both dogs. As the day cooled off, I decided to let Bonnie burn a little more energy off and took her out to the field for a short play with the ball Day 6 had another, slightly longer, trip out to play ball. * * * #### Day 7: Poooooooooooo One the morning of Day 7, I went to get the dogs up, but was hit by a strong smell as soon as I entered the room. As I turned the corner, I saw Bonnie in her pen: had I forgotten to lock the crate last night? I was _sure_ I'd closed it. She was stood, grinning up at me, with the carpet around her covered in shit. Diarrhoea is one of the possible side effects of Rheumocam. Of course, it's _equally_ possible that she found and ate something nasty while in the garden. Whatever the cause was, it made her shit hard enough that, for the first time ever, she'd escaped her crate, breaking the lock in the process: There wasn't any call for immediate concern though: she seemed fine and happy, so I set about cleaning it all up. The prior day's dose had been her last round of painkillers so there were, thankfully, no repeats of this. * * * #### Day 8: Fun With Friends By day 8, Bonnie had been out for a few runs on her own with no sign of discomfort afterwards. But, as she often does when we're out alone, she'd spent a chunk of the time looking around to see if her mates were coming in through any of the field's entrances. So, on day 8, we went back to meeting up with her doggie pals for a run-around. * * * #### Day 10: Black is the new Pink The day had _finally_ come to remove the pink recovery suit. As usual, we put Bonnie on the lead to go for a run, but then I held her head while the suit was unzipped (prompting the expected growling). The suit was carefully pulled down and off her back legs before we released her so that she could step her front legs out herself. Later that night, Bonnie lay on her back on the sofa and we were _finally_ able to get a photo of her (now healed) wound It _really_ is tiny compared to Pepper's scar. * * * #### Day 13 Towards the end of day 13, Bonnie's mood shifted a bit - she became _very_ clingy and cuddly. When Pepper got a bit FOMO, Bonnie stopped "speaking" to her for the rest of the day - when Pepper tried to initiate play, Bonnie turned her back on her and went and curled up in her crate. She wasn't showing any signs of pain or discomfort, so our assumption was that this was the result of hormonal changes. Although Pepper never showed any sign of it, apparently it can take 2-4 weeks for their hormones to settle. * * * #### Day 14 The dogs are back to being friends again, games of bitey face and all. * * * #### Conclusion So, there we have it: Bonnie was able to play again 3-4 days from spaying and was fully recovered within 10. She's no longer at risk from the range of things that can affect intact females (including males breaking in to find her when she's in season) and no longer has to miss out on a month of playing with friends twice a year. Pepper's recovery period had been _much_ longer and included a period where she wasn't even allowed to go on walks. Bonnie's recovery period, whilst exhausting for me7, was **much** better in comparison. If we ever got a third dog (no!), despite the additional cost, I think we'd _definitely_ go for the laparoscopic spay again. We'd also **definitely** go for the surgery suit over a cone-of-shame or (as Pepper had) a loosely tied t-shirt: * Although the suit did make it harder for us to check the wound, a large part of that difficulty came from Bonnie herself - I think we'd have been able to check it with Pep. * Unlike a cone, the suit acted as a barrier to help keep detritus and crap away from the wound. * It also didn't need constant adjustment like Pepper's t-shirt had Apart from day 3 and 10, were were essentially all able to act as if it wasn't even there. We're now a few days beyond day 10 and the only sign that anything even happened is Bonnie's shaved patch. * * * 1. Hence the procedure being known as a "Lap Spay" ↩ 2. Apparently the associated equipment costs between £20-30K and the setup time is quite a bit longer, so it's not like the vet is charging extra just for the sake of it ↩ 3. Awwwwww sweeeeeet ↩ 4. In truth, most dog owners enthuse about any toilet activity that happens outside the house, because that psuedo-excitement helps reinforce the positive behaviour ↩ 5. Sometimes, quite literally ↩ 6. there's a certain irony in having to conduct a _heat_ survey on a dog that's just been spayed. ↩ 7. It would have been worse with Pep, but we only had one dog at the time so there was no need for me to supervise as closely ↩

New #Blog: Bonnie's Lap Spay Diary
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/b...

#dogs #housestuff #vets

0 0 0 0
Preview
Replacing the Valve In a Stuck or Dripping Tap I hate plumbing and, as a result, tend to try and put off any job where a mistake could lead to an inexorable drip-drip that might only become apparent when a ceiling comes down - admittedly, not too likely when repairing a stuck tap. A stuck tap is also harder to ignore (though not impossible, I managed to ignore one for long enough that I ended up with a second one which also needed doing). This short post describes how to replace the valve on a stuck (or dripping) tap - the photos and examples are for a quarter turn tap, but the same process also applies to a twist tap (i.e. one that turns multiple times and uses a compression valve). The entire process (not including getting a replacement valve) takes about ten minutes. * * * #### Valve Sizing Before moving onto the easy practical stuff, let's deal with the more complex bit: understanding sizing. If you're in the UK, there's a _very_ good chance that the valve size that you need has a G 1/2" BSP thread. With that knowledge, in order to avoid ordering the wrong part, you might be tempted to measure your valve to confirm that it is `12.7mm` in diameter. You might even be eyeballing the valve at the moment and thinking "nope, that's got to be 3/4". But, don't be fooled: BSP sizes don't match what you'd measure with a ruler and the diameter of a 1/2" BSP thread is actually more like 20mm. The 1/2 inch measurement relates to the inside diameter of the steel pipe that the thread would originally have been on the outside of. As a rough guide to sizing: BSP size | Rough diameter ---|--- 1/4" | 13mm 1/2" | 20mm 3/4" | 26mm Why do we still use these? Just because... Although not too relevant here, there's also some fun with the letters. You can either play the odds and get a 1/2" BSP valve or wait until the valve is visible and measure it to be sure of the size. * * * ### The Practical Bit The tap in my examples is a standard bathroom basin tap: Before you start, put the plug in so that nothing can get dropped down the drain. To begin, we need to take the lever off the tap - it's probably held on by a screw at the top, but feel around the sides of it in case there's a grub screw (note: if there is, you'll probably need a different valve to those described here). If there's no grub, then the screw is likely to be under a cover, use a thin flathead screwdriver to prise the top off and reveal the screw: Remove the screw and set it aside. Slide the lever up and off the valve - it sits on splines (normally about 20), so may grip a little. You should now be able to see the top of the valve: This is your opportunity to check the size of the valve. Measure the diameter at the widest part and then compare it to the table above - you're looking for a rough rather than exact match (if you want a better match, you'll have to continue to remove the valve). Before proceeding to the next stage, if you haven't already: ISOLATE THE TAP. There may be an isolation valve in the pipe leading to the tap, but if not, you'll need to turn the house supply off. Once done, sit the lever back over the splines and turn the tap on to release any residual pressure. The valve can be unscrewed using a deep socket. You might find that it's extremely tight and that the tap will move instead. You **can** brace against the tap but, if you've got one, it's a lot quicker and easier to use a battery impact gun to buzz it off. Either way, get the valve turning and then loosen and remove it: * * * #### Compression Valves: Cleaning If you've got a compression valve that's sticking, you may not need to replace it. If you look you'll see that there are two parts, the main body and the bit with the washer on. If you turn the splined spindle, you should find that the section with the washer advances until it comes off - you can then use wire wool to clean corrosion off it and the inside of the main part (wrap some wire wool over the end of a screwdriver to really get in there). Apply some silicon grease before screwing the washer section back on and you'll likely find that the valve's good to go for a few more years. * * * #### Refitting Refitting is the reverse of removal, take your new valve: Bung it into your tap-hole and screw it down: Push the tap lever back over the splines, but don't screw it down just yet. Turn the water back on and check that the tap operates (you may get some spitting as air clears out). Then, remove the lever and look for any signs of leakage - you're looking for beads of water around the edge of the valve or (if you've been rough) the tap. Once you're happy, put the lever back on and screw it down. Put the cover back on (I've fitted a new tap lever here): You've probably disturbed a fair amount of limescale and generated debris, so clean around the area, but then you're done!

New #Documentation: Replacing the Valve In a Stuck or Dripping Tap
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/r...

#diy #housestuff #plumbing

0 0 0 0
Preview
Two years of solar We've now had a solar install for 2 years (give or take a couple of days). Although last year's summer wasn't as sunny as the previous year, May 2025 has got this year off to a good start (at least, if you ignore the fact that it's raining as I write this). This post is a _very_ quick look over the install's performance so far. * * * #### Headline Stats Since install, the system has generated 5.67 megawatt hours of electricity: This means that we generated `2.89 MWh` this year, slightly more than in the year before. Taking into account savings generated by charging from the grid and discharging when prices are higher, the system has generated savings of around £1150: The (rolling) average weekly saving is fairly stable, around the £12 mark: * * * #### Battery The battery continues to disappoint, generating average daily savings of £0.67 a day: Unfortunately, this is 32% _lower_ than the same time last year - this is probably partly because there were fewer savings sessions over winter (and those that we did have had far less attractive pricing than the year before) so we didn't have the benefit of dumping the battery to grid at massively inflated export prices. We had a short period at the beginning of the year where the battery was out of service although, to be fair, that was my own fault. Thankfully, I managed to resolve it without needing to contact the manufacturer, avoiding a repeat of them sending my details out and claiming that I'm a battery service tech. * * * #### Exports Our total export income remains quite low We only exported `159kWh` this year (compared to `298kWh` the year before). This drop in export is primarily the result of us using a hot tub as an energy sink. Although we've earned less export income, the energy has been put to good use.

New #Blog: Two years of solar
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/s...

#electrical #housestuff #solar

0 0 0 0
Preview
Doggy Boot Build The Mini is now gone: It failed one time too many, so we sent it to the great scrapyard in the sky (via, uhhh, the smaller and _very definitely_ land based, local scrapyard). The replacement is a Vauxhall Zafira, chosen, in no small part, because the size of its boot works well for a family with 2 dogs (It's an MPV, so there's room for a sixth and seventh seat in there). However, the fold down rear seats meant that the boot couldn't really be used by the dogs without some changes - there are gaps that a paw could easily slip into and cause injury. We've no actual need for the two additional seats, so I decided to board the boot out so that it's primarily a dog transportation area. Note: this project also reminded me that I'm getting old - the last time I thought about boarding out a boot was to put a sound system in there. * * * #### The Plan The boot looks like this: It's not _brilliantly_ visible in the photo, but there's a gap between the top of the laid down seats and those in front (there's also a gap under the orange line that I've unhelpfully drawn over the middle). The plan was to use some plywood to lay a new floor over the top, covering any gaps (including the one at the back). Originally, I intended to cover the plywood in boot carpet, but I quickly realised that that would be a mistake: if one of the dogs were to have a wee, we'd spend the rest of our lives trying to get the smell back out. So, instead, I decided to stick some rubber flooring to the wood, protecting it against liquids and making it _largely_ wipe clean. I also wanted to make sure that the build wouldn't rub at the plastic surroundings, damaging them and squeaking in the process, so I planned to cut the plywood a little smaller and run foam pipe lagging around the edge to provide a buffer. * * * #### The Build After a bit of umming and ahhing, I went for 5mm plywood: there was a trade-off to be made between rigidity (to cover that gap at the back) and not having too severe a lip along the front edge of the boot. I cut the plywood down to a 91 x 104cm sheet: Using spray adhesive, I stuck a strip of rubber flooring to the top of the wood The rubber flooring was 50cm wide, so the second run overlapped the edge a bit. I realised that having an overhang at the front _could_ prove useful, so I didn't trim it all the way back to the wood. To help ensure the rubber wouldn't lift in the middle, I ran a bit of clear sealant where the two sheets met. I also cut down some of the remainder and stuck it underneath the plywood, creating little grippy feet to help ensure the plywood didn't slide around on top of the boot's existing carpet. Then, I trimmed and ran 9mm foam pipe lagging around the sides and back The lagging also serves a second purpose: it helps to ensure that there'll never be a gap which might lead to pinch injuries or broken nails. Not to mention, of course, that it also helped to hide my somewhat messy trimming of the rubber flooring. * * * #### Fitting Hoping that I hadn't screwed up any measurements, I took the floor to the car to install it. It fit tightly inside (note: I hadn't pushed it all the way down when I took this photo): What I hadn't accounted for (because I hadn't noticed) is that the first ~5cm of the boot floor is slightly sloped, so even after the floor was pushed down there was a slight gap. This was helped, though, by the fact that I'd left the rubber a little longer. The length of the rubber overhang also means that it traps, just slightly, under the boot lid when it's closed - so any liquid "spillage" should never touch the carpeted section of the boot. To check that the board safely covered the gap at the back, I put weight on it - it remained solid: the flooring (and glue) had given it some additional rigidity **and** I'd extended the headrests to provide additional support. The next step, then, was to install a mesh boot guard so that the dogs couldn't jump over the top of the seats: Guards are easily available online. MPVs have gone out of fashion and are a bit of a weird shape, so I couldn't find one specifically intended for something like the Zafira, but, one intended for a SUV fit just fine. * * * #### Hiding The Back Seats We were almost ready to go, but there was a problem. The rear seats do not closely touch and there are visible gaps between them: Now, not to put too fine a point on it, but, dogs can be utter idiots - they're basically oversized and particularly toothy toddlers. If there's a gap visible, you can be fairly sure that, at some point, a dog **is** going to try and get through (or _at least_ push some body part into it). They're obviously _not_ going to fit, but the process of trying will involve damage to the seats along with possible injury. I initially toyed with the idea of putting a sheet of plywood across the back, but decided that it'd be quite hard to keep that from squeaking or causing a pinch hazard where it met the floor. Instead, I settled on a **much** simpler solution: a boot liner. The liner is a piece of soft material with straps which go around the bottom of the seat's headrests to hold it up: The manufacturer _claims_ that the material is dog proof (though nothing ever actually is), but the liner's main benefit is that it moves those gaps out of sight (and therefore out of mind). Although they're folded under in this photo, the liner also has flaps to go up the sides as well. I folded them under because I found that they didn't work particularly well with the shape of the Zafira's boot sides (there's a recess to allow room for arms when the now-hidden seats are in use). The rubber floor underneath grips the liner well and ensures that it doesn't slip about. Finally, I used some duct tape to hold the rear seatbelt tongues up and out of the way. I would have liked to tape the entire belt back, but Vauxhall seem to have found and built the boot out of the one material on earth that black nasty won't stick to. The tension on the belts does keep them quite firmly against the side walls, but we'll keep an eye on them to make sure they don't become a tangle hazard. Whilst we _could_ cut them out, it'd destroy any resale value on the car (who's going to buy an MPV where the additional seats can't be used?), so the worst case is probably that I have to build sides to box them in. * * * #### Conclusion Whilst I'm not _exactly_ delighted that we've got another Vauxhall, it's size means that it **is** pretty well suited to our current needs (and I definitely **am** delighted to be shot of the Mini). Boarding the boot out wasn't a particularly massive project and means that we now don't have to worry about paws slipping down any gaps in the floor. The rubber coating means that we don't need to worry (too much) about liquid spillages and if the need ever arises we can, with a little bit of effort, lift the new floor back out (that sloped bit gives access to the towing loop and the bolt which holds the spare wheel on). With temperatures starting to rise in the UK, we'll probably also look at putting some curtains over the rear windows to ensure that the floor doesn't warm up too much if the car ends up sat in the sun.

New #Blog: Doggy Boot Build
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/c...

#cars #dogs #housestuff #vehicles

0 0 0 0
Preview
Manually Charging Our US2000C Solar Battery Earlier this month, I discovered a couple of issues with our Solar setup. Two apparent bugs had combined and resulted in our solar battery going completely flat: so much so, that the battery couldn't power it's own Battery Management System (BMS). Without a functional BMS, the inverter won't send a charge to the battery, so it wasn't possible to recover from this situation with the inverter alone. Instead, I had to climb up to the battery and use a lithium battery charger to charge one of the battery units. This post talks about _why_ our Pylontech US2000C batteries went dead as well as describing what I did to get them back into service. * * * ### What Went Wrong? #### Bug 1: My Stuff Earlier this year, I switched from using the inverter's static schedule to having Home Assistant dynamically manage our charge schedules. In my post about that process, I noted that it also involved a change in paradigm: > It's worth noting, however, that this _does_ come at a small cost, because we've changed paradigm a bit. > > Previously, the configured schedule was stored in the inverters registers, so would apply whether or not there was connectivity between the inverter and Soliscloud. > > Homeassistant, though, sends an event when something causes it to start - so if connectivity is down, the inverter won't switch the battery over to charging. Almost inevitably, fate jumped up and bit me on the arse. * * * ##### It's Always DNS On the 24th of January, Docker's built in DNS service stopped resolving names, which left the auth proxy container unable to resolve Soliscloud's address: urllib3.exceptions.NameResolutionError: <urllib3.connection.HTTPSConnection object at 0x7f41aca32850>: Failed to resolve 'www.soliscloud.com' ([Errno -3] Try again) As a result, Home Assistant was unable to tell the inverter to start charging the battery from grid, meaning that the only thing that would charge the battery was excess energy from our solar panels. Excess solar energy... January in the UK... * * * ##### Where is Alerting? By this point, you might be tutting and observing that _maybe_ I should have been monitoring things. I had monitoring in place but (you guessed it), Grafana was running on the same box and so Docker's breakage had left it unable to send notifications: logger=ngalert.notifier.alertmanager org=1 t=2025-01-24T15:08:20.074946486Z level=error component=alertmanager orgID=1 component=dispatcher msg="Notify for alerts failed" num_alerts= 1 err="Ben/email[0]: notify retry canceled due to unrecoverable error after 1 attempts: failed to send email: dial tcp: lookup smtp.<redacted>: i/o timeout" I _might_ have caught the issue myself if I'd looked at solar stats, but given the lack of sunshine, I wasn't really looking. * * * #### Bug 2: The Inverter Although I didn't really write about it in my previous post, the inverter has (what should have been) a safety net. The inverter settings include an option called `Forcecharge SOC` If the battery's charge level drops below 18%, the inverter should start charging from grid to bring it back up. This means that, in theory, even if my charge scheduling failed the battery should always have remained in a serviceable state. However... it seems that this setting didn't kick in. * * * #### The Battery's Death The battery stopped being charged on 24th of Jan, however it was fine (in a "not completely dead yet" sense) until the early hours of 31st Jan. Although the reported charge level had been hovering around 20% for days, it plummented overnight and continued to drop during the day: The Voltage and Current graphs indicate that this wasn't the result of some load suddenly being put onto it. The spikes in current and voltage align with increases in the reported SoC indicating that the forcecharge _probably_ was kicking in. Although there's no direct correlation with the drop, temperature graphs show that it got quite cold overnight that night Whilst the temperature was still well within the stated serviceable range of the battery, it's not the first time that I've had suspicions about these batteries and temperature. Temperature issues _would_ also explain why the inverter didn't start force-charging: if the battery got cold enough to switch into protection mode, the inverter would not have been _able_ to charge it. Whether it was the cold, or the battery packs just giving up after a week of not being charged, the battery was now down. * * * #### Powering the batteries on Hoping to be able to convince the inverter to start a charge, I climbed up to the batteries and flipped switches to switch them back on. As soon as they powered on, the alarm light started to flash before the units eventually powered themselves back off. During that time though, although it couldn't charge them, the inverter **did** manage to collect stats (I did this a few times for comparison): The manual for the Pylontechs says that the BMS will not operate if the battery voltage reports below `44.5V`. The highest value on that voltage graph, unfortunately, is `44.1V`. * * * ### Manually Charging Given that the batteries were too depleted for the inverter to start charging them, I was left with two options 1. "Jump" them with another battery 2. Plug them into a wall charger The aim of both is to pull the voltage up just enough that the battery is operable, allowing the inverter to take care of the rest. If I was a battery technician, I'd probably have a spare battery for option `1`, but (contrary to reports) I'm not. This meant that I needed to find a way to charge at least one of battery units myself. Note: I don't know if you've ever googled "how to charge a solar battery", but the results are not _particularly_ helpful for this (answers: use solar, use an inverter etc). One thing that I **did** know was that it was important to use a charger designed for lithium batteries. Chargers intended for lead-acid batteries tend to pulse, which lithium packs don't like (lithium battery packs are also arsonists: if they don't like something, they tend to try and burn it). The user manual provided some additional useful information Item | Value ---|--- Battery Type | LiFePO4 Nominal Voltage | 48V Charge Voltage | 52.5 ~ 53.5V After a bit of hunting around I managed to track down and buy a charger. * * * #### Making a Lead The charger that I'd bought came with crocodile clips, but Pylontech batteries have a waterproof shroud around their posts, preventing the clips from connecting onto them. I found some pylontech to ring connector leads online: these are what are used to connect the battery to an inverter: I cut the clamps off my charger, stripped the cable back and then wrapped and soldered it onto the ring connectors. Then I wrapped both in electrical tape, pulling the charger wire up a little to act as a form of strain relief * * * #### Connecting The Charger I climbed a ladder up to the batteries, taking a powder fire extinguisher and one end of an extension lead (the other was still unplugged downstairs) with me. To make things safe, I isolated the panels Then I isolated the AC supply into the inverter Each of the battery units has a power switch on the left hand side: I switched each of these off. My aim was to partially charge one of the packs: just enough to pull the voltage up so that the inverter could ultimately take over. To avoid having to reach over the other packs, I chose the one nearest to me and disconnected the cabling. Note that the comms cable has also been removed Then I prepared the charger: * Plugged its mains lead firmly into the back of it * Connected its mains lead to the extension lead * Pushed its negative connector onto the negative post on the right * Pushed its positive connect onto the positive post on the left (i.e. the one without the cover) I flipped the battery's power switch back on before pressing the red button for about 2 seconds. The battery lit up and started flashing it's alarm light. One of the charger's LEDs lit up, indicating that it was correctly connected. I carefully stepped over it, climbed down the ladder and plugged the other end of the extension lead into the mains. The charger's second LED lit up to indicate that it was now charging The battery's alarm light had stopped blinking and `RUN` was now illuminated. The first LED on the charge level indicator was flashing It was working! I left the battery to charge for about 90 minutes before checking back in on it. The first charge LED was now solid, with the second flashing. According to the manual, each LED is "worth" 16% of charge, so I knew that the battery was charged to at least that level. * * * #### Switching Back Over I climbed back down the ladder, switched off (and unplugged) the charger before climbing back up, turning the battery off and then disconnecting the charger from the battery. Being careful to make sure that each cable went back into the correct place, I connected the unit back to the others. I flipped the power switch on each and then held the red button down on the master (the one without a cable in link port 0 - the top battery in my case). One by one, the units powered on. The first two very briefly flashed an alarm light, but then their `RUN` light illuminated The battery was online! Knowing that the charge may not last long, I quickly edited the inverter's settings to trigger a grid charge. The inverter kicked the charge in immediately: I let the charge continue until the battery was reporting around a 30% charge. After I switched the charge off, the batteries began to supply the house until they reached 20% and discharging stopped: I did this partial charge & discharge for a couple of reasons 1. I wanted to be sure that the battery **did** stop at 20% 2. Battery balancing: the manually charged battery was slightly more charged than the others - I reasoned that discharging would probably help even this back out With the battery appearing to be working correctly, I left the inverter to kick the overnight charge in automatically (good timing too, we've _finally_ had some cheap leccy prices again!). Checking the graph the next morning showed that things had behaved as expected * * * ### Avoiding Repeat Incidents Once I realised the battery was dead _and why_ , my initial reaction was to want to turn all of the smarts back off and accept occasionally spending a couple of pence extra. **But** , that was almost certainly an over-reaction because the issue can just as easily be mitigated by * Having a once daily charge configured on the inverter * Turning alarm notifications on in Soliscloud * Reconfiguring containers not to use Dockers DNS service I did also toy with the idea of having a script poll Grafana's API to list active alerts, but that would have created a lot of routine noise, driving alert fatigue. * * * #### Conclusion Urgg, just urgg... although the inverter's forcecharge setting _should_ have saved me, this was my own fault really: I'd _even_ identified that the change of paradigm brought some risk, but hadn't considered some of the more extreme outcomes. The fact that I've had to buy a charger (and leads) means that the scheduling changes I made will now very likely never break even (though we shall have to see). Given that this is the second time I've suspected that low temperatures had a hand in issues with our battery, I'm going to run a temperature probe up to the batteries themselves so that there are more granular stats the next time that there's an issue. It does seem though, that fate is determined that I should one day become a battery technician. But, I'm not ready to cell out yet.

New #Blog: Manually Charging Our US2000C Solar Battery
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/r...

#battery #electrical #housestuff #pylontech #solar #troubleshooting

0 0 0 0
A photo of a wooden door with a glued on pane of clear acrylic glass as a protection layer against dog claw marks.

A photo of a wooden door with a glued on pane of clear acrylic glass as a protection layer against dog claw marks.

I added a pane of acrylic glass to the inside of my door so that Lucas doesn't further ruin it when I need to leave him alone at home.

He scratches the door and since it's not technically mine but my landlord's, I decided to glue a protective pane on it.

#housestuff #dailydoglife #diy

10 0 2 0
Preview
Setting Up Zigbee2MQTT With HomeAssistant <img class="introimage" itemprop="image" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/usb_coordinator.jpg" style="display: none"/><div> <p>I've run a network of Zigbee devices for quite some time now. Although we've got a few wi-fi smart sockets, I <em>prefer</em> Zigbee because it's entirely local and doesn't leave our stuff <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/heating-with-electric-instead-of-gas.html#access-to-the-manufacturers-platform">reliant on someone else's cloud service</a>.</p> <p>Our setup has been <em>fairly</em> reliable but we periodically have issues with messages getting lost. </p> <p>This only seems to affect devices which intermittently send (or receive) rather than regularly sending updates. This means that messages for devices like door contacts and <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/adding-remote-start-to-our-ancient-dishwasher.html">fingerbots</a> don't always make it through.</p> <p>Having used the same co-ordinator for about 5 years, I decided to try and address this issue by switching to a newer and more powerful one.</p> <p>This post talks about setting <a href="https://www.zigbee2mqtt.io">Zigbee2MQTT</a> up to talk to a Hamgeek HMG-01 Plus and <a href="https://www.home-assistant.io/">Home Assistant</a> (via a <a href="https://en.wikipedia.org/wiki/MQTT">MQTT</a> broker).</p> <p>Note: I <a href="https://www.bentasker.co.uk/posts/blog/general/migrating-from-homeassistant-os-to-homeassistant-in-docker.html">no longer run HAOS or a Supervised setup</a>, so this post describes setting the relevant components up separately. If you <strong>are</strong> running HAOS/Supervised, there's <a href="https://github.com/zigbee2mqtt/hassio-zigbee2mqtt">an official addon</a> which can be installed instead.</p> <!-- TEASER_END --> <hr/> <h3 id="contents">Contents</h3> <div class="toc"> <ul> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#contents">Contents</a></li> <li> <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#the-hardware">The Hardware</a><ul> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#the-original-coordinator">The Original Coordinator</a></li> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#the-new-zigbee-bridge">The New Zigbee Bridge</a></li> </ul> </li> <li> <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#setup">Setup</a><ul> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#the-plan">The Plan</a></li> <li> <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#connecting-to-mqtt">Connecting to MQTT</a><ul> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#zigbee2mqtt-setup">Zigbee2MQTT Setup</a></li> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#connection-ping-error">Connection Ping Error</a></li> </ul> </li> <li> <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#connecting-homeassistant">Connecting HomeAssistant</a><ul> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#auto-discovery-breakage">Auto-Discovery Breakage</a></li> </ul> </li> </ul> </li> <li> <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#moving-devices-between-networks">Moving Devices Between Networks</a><ul> <li> <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#updating-home-assistant-config">Updating Home Assistant Config</a><ul> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#button-push-automations">Button Push Automations</a></li> </ul> </li> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#unsupported-devices">Unsupported Devices</a></li> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#making-better-use-of-the-mesh">Making Better Use Of The Mesh</a></li> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#disabling-zha">Disabling ZHA</a></li> </ul> </li> <li><a href="https://www.bentasker.co.uk/posts/blog/house-stuff/moving-my-zigbee-network-from-zha-to-zigbee2mqtt.html#conclusion">Conclusion</a></li> </ul> </div> <hr/> <h3 id="the-hardware">The Hardware</h3> <h4 id="the-original-coordinator">The Original Coordinator</h4> <p>We've used the same zigbee coordinator since first getting started - it's a (bare) USB with a CC2531 chip on it:</p> <p><img alt="Photo of the original coordinator, it's a bare USB stick cable tied onto the front of a metal rack" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/usb_coordinator.jpg"/></p> <p>I bought the stick about 5 years ago for £15, so we've <em>definitely</em> got our moneys worth out of it.</p> <p>Use of this stick brings a drawback on top of the message loss: It has to be physically connected to my HomeAssistant instance, restricting my ability to move HomeAssistant between hardware.</p> <p>So, I wanted the replacement to be network rather than direct attached.</p> <hr/> <h4 id="the-new-zigbee-bridge">The New Zigbee Bridge</h4> <p>The bridge that I ordered is a Hamgeek HMG-01 Plus:</p> <p><img alt="A photo of the HMG-01 Zigbee to ethernet bridge. It's a small black box with a big black antenna on the back" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/hamgeek_HMG-01.jpg"/></p> <p>At £75, it was decidedly more expensive than the USB stick that it was replacing but, I hoped, would help with the network's reliability.</p> <p>The box acts as an ethernet to Zigbee bridge: software wishing to interact with the Zigbee network can connect to a raw TCP port and send messages over that.</p> <p>It's running <a href="https://github.com/xyzroe/xzg">XZG</a>, which has the ability to connect to a Wireguard VPN and/or to publish directly into a MQTT broker:</p> <p><img alt="Screenshot of the web interface. There are options for VPN and MQTT on the side" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/xzg_interface.png"/></p> <p>I <em>had</em> hoped that the MQTT support would mean that I didn't need to run a separate Zigbee2MQTT instance. However, that didn't really pan out (more on that later).</p> <hr/> <h3 id="setup">Setup</h3> <h4 id="the-plan">The Plan</h4> <p>The initial plan was quite simple:</p> <ol> <li>Connect the box to the network</li> <li>Have it publish into my <a href="https://mosquitto.org/">mosquitto broker</a> (I described setting that up <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/connecting-my-smart-meter-to-influxdb-using-telegraf-and-a-glow-display.html#running-mosquitto">here</a>)</li> <li>Use <a href="https://www.home-assistant.io/integrations/mqtt#mqtt-discovery">MQTT Discovery</a> to have devices appear in Home Assistant</li> <li>Move Zigbee devices over</li> </ol> <p>The plan involved running the two networks side by side for a little while and manually moving things around.</p> <p>Manual moves were necessary because switching out a Zigbee coordinator <em>isn't</em> as straightforward as replacing a wifi router - you can't generally just configure it to advertise the original network and have it work.</p> <p>I was also going to move from using <a href="https://www.home-assistant.io/integrations/zha/">ZHA</a> to using <a href="https://www.zigbee2mqtt.io/">Zigbee2MQTT</a>. This wasn't the result of any particular issues with ZHA (I've been quite happy with it), but simply a reflection of Home Assistant not allowing you to configure more than one ZHA connection at a time: I couldn't run side-by-side <strong>and</strong> use ZHA for both.</p> <hr/> <h4 id="connecting-to-mqtt">Connecting to MQTT</h4> <p>Physically connecting the box to the network was, of course, straight forward. It has DHCP enabled by default, so I just needed to log into <a href="https://www.bentasker.co.uk/posts/blog/general/opnsense-pfsense-fttp-and-1gbps-pppoe.html">my OpnSense Router</a> and look at which IP had been allocated (and make the assignment permanent).</p> <p>The HMG-01 exposes a simple web-interface which I logged into before configuring it to connect to my Mosquitto instance:</p> <p><img alt="Screenshot of the MQTT settings page" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/mqtt_settings.png"/></p> <p>Unfortunately, it failed to do so.</p> <p>There was no obvious reason for this failure - mosquitto wasn't logging anything untoward, and the HMG's web interface doesn't <em>really</em> do logs (there <em>is</em> a debug console, but it dumps data in hex - not the most convenient thing to use).</p> <p>The web interface <strong>was</strong> notifying me that there was a firmware update available, so I decided to install that in the hope that it'd fix things.</p> <p>Unfortunately, installation of <em>that</em> failed too: the progress bar got to about 98% and then just hung, with the HMG-01 going unreachable on the network.</p> <p>I waited a while and then power cycled it hoping that I hadn't bricked the thing. Thankfully, it came straight back up, albeit still running the old firmware. I tried a firmware update again - this time it didn't hang, but it still didn't manage to install the update.</p> <hr/> <h5 id="zigbee2mqtt-setup">Zigbee2MQTT Setup</h5> <p>Rather than repeatedly banging my head against the same wall, I decided to set up a separate Zigbee2MQTT instance instead. The web interface on the HMG-01 can even generate the necessary configuration for you:</p> <pre><code class="language-yaml"># Serial settings serial: # Location of XZG port: tcp://192.168.5.174:6638 baudrate: 115200 # Disable Zigbee led? disable_led: false # Set output power to max 20 advanced: transmit_power: 20 </code></pre> <p>I started by creating somewhere for the config to live:</p> <pre><code class="language-sh">mkdir -p docker_files/zigbee2mqtt/data nano docker_files/zigbee2mqtt/data/configuration.yaml </code></pre> <p>The <a href="https://www.zigbee2mqtt.io/guide/getting-started/">docs</a> include some extra stuff, so I merged it with what the HMG-01 had given me:</p> <pre><code class="language-yaml">version: 4 mqtt: base_topic: zigbee2mqtt server: mqtt://192.168.5.5:1883 user: zigbee password: NotMyRealPassword serial: port: tcp://192.168.5.174:6638 baudrate: 115200 advanced: transmit_power: 20 frontend: enabled: true homeassistant: enabled: true </code></pre> <p>I then just needed to spin up a container:</p> <pre><code class="language-sh"># Zigbee2MQTT exposes a web interface on port 8080 docker run \ -d \ --name=zigbee2mqtt \ -v $PWD/docker_files/zigbee2mqtt/data:/app/data \ -p 8080:8080 \ --restart=always \ koenkk/zigbee2mqtt </code></pre> <p>The container started up and then <strong>immediately</strong> failed, logging the following:</p> <pre><code class="language-text">error: z2m: Exiting... error: z2m: Error: Cannot discover TCP adapters at this time. Specify valid 'adapter' and 'port' in your configuration. </code></pre> <p>The cause was <em>reasonably</em> obvious: the <code>zigbee2mqtt</code> docs include an <code>adapter</code> attribute, whilst the config generated by the HMG-01 doesn't.</p> <p>What <em>wasn't</em> immediately clear, though, is which adapter type I needed to specify: Zigbee2MQTT supports <a href="https://www.zigbee2mqtt.io/guide/adapters/">a variety of types</a>.</p> <p>The status page on the HMG-01 helped to clear that up:</p> <p><img alt="Status page says the HMG-01 is rocking a CC26252P7" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/hardware_type.png"/></p> <p>Cross-referencing that with the Zigbee2MQTT docs shows that it's a <a href="https://www.zigbee2mqtt.io/guide/adapters/zstack.html">zstack</a> adapter, so I updated the <code>serial</code> section of the config file and restarted the docker container:</p> <pre><code class="language-yaml">serial: port: tcp://192.168.5.174:6638 baudrate: 115200 adapter: zstack </code></pre> <p>Good news! It connected!</p> <hr/> <h5 id="connection-ping-error">Connection Ping Error</h5> <p>Bad news... it only connected for about a minute, and then exited with a new error message</p> <pre><code class="language-text">Error: SRSP - SYS - ping after 6000ms </code></pre> <p><a href="https://www.zigbee2mqtt.io/guide/installation/20_zigbee2mqtt-fails-to-start_crashes-runtime.html#error-srsp-sys-ping-after-6000ms-for-zstack-or-host-fatal-error-for-emberznet">The docs</a> note that this could be a firmware issue:</p> <blockquote> <p>If you are using a CC2530 or CC2531; it is a common issue for this adapter to crash (due to its outdated hardware). Reflashing the firmware should fix the problem</p> </blockquote> <p>So, I was straight back to having to get the HMG-01 to update its firmware.</p> <p>In order to try and start from a clean slate, I power cycled the HMG-01, logged in and selected to update the Zigbee firmware:</p> <p><img alt="Screenshot of the Zigbee update screen in the web interface, the most recent update is 10th July 2024" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/zigbee_ota_update.png"/></p> <p>This time, it installed and the box rebooted, reporting the new version when it came up.</p> <p>Next, I installed OS updates, hoping that they wouldn't get stuck this time around:</p> <p><img alt="Screenshot of the ESP32 updates page, latest update is 14th Sept 2024" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/esp32_update.png"/></p> <p>It went straight through and, one reboot later, the box was reporting the correct version.</p> <p>The interface also indicated that Zigbee2MQTT had now successfully connected to it.</p> <hr/> <h4 id="connecting-homeassistant">Connecting HomeAssistant</h4> <p>The next step was to get HomeAssistant looking at MQTT.</p> <p>This was already <em>mostly</em> set up from when I <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/connecting-my-smart-meter-to-influxdb-using-telegraf-and-a-glow-display.html#linking-to-homeassistant">hooked my Glow IHD up</a>, but to recap:</p> <ul> <li> <code>Settings</code> </li> <li> <code>Device and Integrations</code> </li> <li> <code>Add Integration</code> </li> <li><code>MQTT</code></li> <li><code>MQTT</code></li> <li>Enter broker details</li> <li>Hit Submit and then Finish</li> </ul> <p>HomeAssistant has MQTT autodiscovery enabled by default, so everything should be in place.</p> <p>The way that auto-discovery works is simple, but clever: When Zigbee2MQTT adds a new <em>thing</em> it publishes a message into a topic beginning with <code>homeassistant</code>: </p> <pre><code class="language-text">info: z2m:mqtt: MQTT publish: topic 'homeassistant/light/0x00158d000507f0c1/light/config', payload '{"availability":[{"topic":"zigbee2mqtt/bridge/state","value_template":"{{ value_json.state }}"}],"brightness":true,"brightness_scale":254,"command_topic":"zigbee2mqtt/bedroom_light/set","device":{"hw_version":1,"identifiers":["zigbee2mqtt_0x00158d000507f0c1"],"manufacturer":"Innr","model":"B22 bulb dimmable","model_id":"BY 165","name":"bedroom_light","sw_version":"2.0","via_device":"zigbee2mqtt_bridge_0x00124b002e1249ab"},"effect":true,"effect_list":["blink","breathe","okay","channel_change","finish_effect","stop_effect"],"name":null,"object_id":"bedroom_light","origin":{"name":"Zigbee2MQTT","sw":"2.0.0","url":"https://www.zigbee2mqtt.io"},"schema":"json","state_topic":"zigbee2mqtt/bedroom_light","unique_id":"0x00158d000507f0c1_light_zigbee2mqtt"}' </code></pre> <p>HomeAssistant subscribes and creates devices, entities and actions when these messages appear.</p> <hr/> <h5 id="auto-discovery-breakage">Auto-Discovery Breakage</h5> <p>I was ready to add my first zigbee device to the new network - I'd specifically bought a new sensor so that I could test without the complexity of adding something which had previously been on a different network.</p> <p>After putting the device into pairing mode, I connected to the Zigbee2MQTT web interface and clicked <code>Permit Join</code>. Within seconds, the new device appeared and notifications popped up to say that it had completed interview.</p> <p>But... the device <strong>didn't</strong> appear in Home Assistant.</p> <p>Nothing in Home Assistant's UI suggested an issue, the device just wasn't appearing.</p> <p>I double checked the MQTT integration's configuration (you need to click <code>Reconfigure MQTT</code> to get at the settings) and Auto Discovery was <em>definitely</em> enabled:</p> <p><img alt="MQTT auto discovery is definitely enabled in HomeAssistant" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/discovery_enabled.png"/></p> <p>I hadn't changed any of the other settings, so it <em>should</em> have been working.</p> <p>Home Assistant's log pointed me towards the issue:</p> <pre><code class="language-text">ERROR (MainThread) [homeassistant.components.mqtt.mixins] Error 'extra keys not allowed @ data['device']['model_id']' when processing MQTT discovery message topic: 'homeassistant/update/0x282c02bfffeac0e6/update/config', message: '{'availability': [{'topic': 'zigbee2mqtt/bridge/state', 'value_template': '{{ value_json.state }}'}], 'command_topic': 'zigbee2mqtt/bridge/request/device/ota_update/update', 'device': {'hw_version': 0, 'identifiers': ['zigbee2mqtt_0x282c02bfffeac0e6'], 'manufacturer': 'Third Reality', 'model': 'Wireless motion sensor', 'model_id': '3RMS16BZ', 'name': 'hallmotionsensor', 'sw_version': 'v1.00.79', 'via_device': 'zigbee2mqtt_bridge_0x00124b002e1249ab'}, 'device_class': 'firmware', 'entity_category': 'config', 'entity_picture': 'https://github.com/Koenkk/zigbee2mqtt/raw/master/images/logo.png', 'json_attributes_template': '{"in_progress": {{ iif(value_json[\'update\'][\'state\'] == \'updating\', \'true\', \'false\') }} }', 'json_attributes_topic': 'zigbee2mqtt/hallmotionsensor', 'latest_version_template': "{{ value_json['update']['latest_version'] }}", 'latest_version_topic': 'zigbee2mqtt/hallmotionsensor', 'name': None, 'object_id': 'hallmotionsensor', 'origin': {'name': 'Zigbee2MQTT', 'sw_version': '2.0.0', 'support_url': 'https://www.zigbee2mqtt.io'}, 'payload_install': '{"id": "0x282c02bfffeac0e6"}', 'state_topic': 'zigbee2mqtt/hallmotionsensor', 'unique_id': '0x282c02bfffeac0e6_update_zigbee2mqtt', 'value_template': "{{ value_json['update']['installed_version'] }}", 'platform': 'mqtt'}' </code></pre> <p>It was complaining that the payload contained an unexpected attribute (<code>model_id</code>).</p> <p>I checked and found that I was running quite an old Home Assistant image (from March last year). Presuming that support for this attribute might have been added in a more recent release, I updated the container to run the latest (<code>2025.1.3</code>).</p> <p>By the time that Home Assistant came back up, the new device was present and working.</p> <p><img alt="Screenshot of the MQTT integration page, showing the motion sensor that I added" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/motion_sensor.png"/></p> <p>This issue also highlighted that I'd <em>probably</em> dodged a bullet by not spending too much time trying to get the HMG's built-in MQTT support working: If some future backwards incompatible-change is made to Home Assistant's auto-discovery, I'll only need to update <code>zigbee2mqtt</code> rather than having to wait (and hope) for a firmware update.</p> <hr/> <h3 id="moving-devices-between-networks">Moving Devices Between Networks</h3> <p>Now that I had a working network, it was time to start moving devices over.</p> <p>If you're here looking for an easy way to do this, I've got some bad news: there isn't one and you're likely going to need to go round holding buttons to factory reset your Zigbee devices.</p> <p>Admittedly, this isn't true of all devices - I did have <em>some</em> luck in clicking <code>Remove Device</code> in Home Assistant's ZHA config:</p> <p><img alt="Screenshot of the location of the Remove button in ZHA config" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/remove_button.png"/></p> <p>However, most devices wouldn't join a new network when kicked out of the original and needed a physical intervention to reset them.</p> <p>The way to factory reset devices basically fall into two groups</p> <ul> <li>Anything with a button: hold for 10 seconds (normally resulting in an LED flashing)</li> <li>Light bulbs: flick the power on and off quickly 6 times (resulting in the bulb flashing)</li> </ul> <p>The most annoying variation on these is devices with a recessed reset button, requiring a pokey device to press and hold them.</p> <p>Just before resetting a device, I opened the Zigbee2MQTT web interface on my phone and clicked <code>Permit Join</code></p> <p><img alt="Screenshot of the Zigbee2MQTT web interface's navigation bar" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/permit_join.png"/></p> <p>After being reset, each device appeared in the device list.</p> <p>When they first appear, their "friendly name" is set to their IEEE address - something like <code>0x282c01bfffaac0f6</code> - which obviously isn't all that human friendly.</p> <p>I edited the name, telling Zigbee2MQTT to also update the entity ID in Home Assistant (which works by sending messages into the auto discovery topic):</p> <p><img alt="Screenshot of the Zigbee2MQTT rename device screen. There's an option to have it also update the entity ID in home asssistant" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/rename_device.png"/></p> <p>I started by resetting anything which I knew could act as a Router - the idea being that it would extend the mesh (and therefore coverage), making it easier to get simple sensors to pair.</p> <hr/> <h4 id="updating-home-assistant-config">Updating Home Assistant Config</h4> <p>Still working from my phone, as I paired each new device, I went into the ZHA configuration and removed it. This didn't serve any immediate purpose other than to allow the list to act as a "still todo" list.</p> <p>I also looked for automations and scripts which referenced the original entities so that I could update them to use the newly discovered devices, only moving onto pairing the next device once I was happy I'd got everything.</p> <p>One tip that I discovered a little too late: If you restart Home Assistant, it'll log an error for any automation which references an invalid device:</p> <pre><code class="language-text">ERROR (MainThread) [homeassistant.components.automation] Automation with alias 'Summer House Notify Too Hot' failed to setup triggers and has been disabled: Unknown device 'a90c8ee913f427fc150d1c21f6cbe822' </code></pre> <p>This is obviously <em>quite</em> useful for verifying whether you've missed anything once you've finished removing devices.</p> <hr/> <h5 id="button-push-automations">Button Push Automations</h5> <p>I remembered that it had originally been <a href="https://www.bentasker.co.uk/posts/blog/general/712-musings-on-home-automation.html#zigbeeevents">a bit of a pain</a> getting automations to fire in response to button pushes, so once I came to the first button I slowed down.</p> <p>With ZHA, I'd had to create an event based automation which triggered on a (fairly generic) event and then filtered based on the payload:</p> <pre><code class="language-yaml">- id: '1608742755182' alias: Shower Room Button Press description: '' trigger: - platform: event event_type: zha_event condition: - condition: template value_template: '{{ trigger.event.data.device_ieee == "5c:02:72:ff:fe:49:f2:ef"}}' action: - choose: - conditions: - condition: template value_template: '{{ trigger.event.data.command == "on" }}' sequence: - service: script.towel_rail_power_on data: {} default: - service: script.towel_rail_power_off data: {} mode: single </code></pre> <p>Button press automations are <strong>far</strong> simpler with Zigbee2MQTT - the first time that it sees something happen, it pushes a message to have auto-discovery attach an action type to the device.</p> <p>So, after pairing the button, the first thing that I needed to do was to <em>push it</em>.</p> <p><img alt="Gif: push the button " src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/push_the_button.gif"/></p> <p>Home Assistant's automation builder then exposed <code>"single" action</code> as a trigger-able event:</p> <p><img alt='The trigger block in an automation. Device hall_pushbutton as action "single"' src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/automation_trigger.png"/></p> <p>Double-pressing the button similarly led to a <code>double</code> action also becoming available.</p> <hr/> <h4 id="unsupported-devices">Unsupported Devices</h4> <p>When I came to pair the smart-socket in our garage, I ran into something new: Zigbee2MQTT reported that the device wasn't supported.</p> <p><img alt="Screenshot from the interface showing that it's not supported" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/unsupported_device.png"/></p> <p>Clicking the <a href="https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html">how_to_add_support</a> link showed that this <em>sounds</em> more concerning than it actually is. The socket works just fine as a Zigbee device (and still appears in Home Assistant), it's just that Zigbee2MQTT lacks a definition of how the device should be presented.</p> <p>The docs link describes how to extract information and raise a PR - something that I'll do once I've found a suitable product image for it.</p> <hr/> <h4 id="making-better-use-of-the-mesh">Making Better Use Of The Mesh</h4> <p>One thing that I noticed quite early on was that most of my sensors were connecting directly to the coordinator:</p> <p><img alt="Map of the mesh network - sensors tend to connect direct to the coordinator" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/mesh.png"/></p> <p>The numbers on the lines show link quality, some of the sensors could <em>quite clearly</em> benefit from connecting via a router.</p> <p>They'll probably move around over time, but it's also possible to force a sensor to pair with a specific repeater: by kicking it back out of the network and then choosing the desired repeater in the <code>Permit Join</code> dropdown:</p> <p><img alt="Screen shot of the permit join button, I've selected bedroom_tv" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/permit_join_custom.png"/></p> <p>When it rejoins, it'll connect via the specified router</p> <p><img alt="Routing has been updated and various devices now mesh via their local router" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/final_mesh.png"/></p> <hr/> <h4 id="disabling-zha">Disabling ZHA</h4> <p>With all of my devices moved across, it was time to remove ZHA from Home Assistant.</p> <p>To do this, I went into the ZHA integration and clicked the three dots by the coordinator</p> <p><img alt="Screenshot of the Integration entries. There's a three dot menu on the right hand side" src="https://www.bentasker.co.uk/images/BlogItems/zigbee2mqtt_setup/integration_entries.png"/></p> <p>Clicking <code>Delete</code> removed the entry and when I went back to the Integrations view ZHA was no longer listed.</p> <p>Finally, I needed to recreate the docker container <em>without</em> passing the adaptor through. The original docker invocation included:</p> <pre><code class="language-sh">--device /dev/serial/by-id/usb-Texas_Instruments_TI_CC2531_USB_CDC___0X00124B011949A347-if00:/dev/serial/by-id/usb-Texas_Instruments_TI_CC2531_USB_CDC___0X00124B011949A347-if00 </code></pre> <p>So, I re-ran it without:</p> <pre><code class="language-sh">docker run \ -d \ --name=homeassistant \ -v ${PWD}/docker_files/homeassistant/config:/config \ -p 8123:8123 \ --restart=unless-stopped \ ghcr.io/home-assistant/home-assistant:2025.1.3 </code></pre> <p>Once Home Assistant was up, I unplugged the adaptor.</p> <p>Now that it has no ties to the hardware, I can look at moving Home Assistant into my k8s cluster.</p> <hr/> <h3 id="conclusion">Conclusion</h3> <p>It's something that I've grumbled about (<a href="https://www.bentasker.co.uk/posts/blog/general/712-musings-on-home-automation.html">at length</a>) before, but nothing in home automation <em>ever</em> seems to be straightforward. For all the potential of automation, implementations often lack the polish that we've come to expect in consumer devices.</p> <p>Measured against that (somewhat low) bar, though, moving my network over to the new coordinator and stack wasn't too bad. It's true that there were some hiccups at the start, but some of those were the result of me not having updated Home Assistant in a while.</p> <p>Moving Zigbee devices between coordinators <em>was</em> a bit of a pain, because it meant having to walk around the house holding reset buttons. However, it also presented an opportunity to replace batteries, so I <em>probably</em> shouldn't mind too much.</p> <p>So far, things look good: there's been no sign of message loss, with one-shot devices working exactly as they should.</p> <p>Zigbee2MQTT also exposes a bunch of functionality which ZHA does not. In particular, the ability to bind devices to one another so that (for example) a button can turn lights on even if the co-ordinator and/or Home Assistant are down. At some point I'll get around to playing with that properly.</p> <p>Finally, I've alluded to it a couple of times in this post, but it seems worth saying explicitly: The Zigbee2MQTT documentation really is <strong>absolutely fantastic</strong>.</p> </div>

New #Blog: Setting Up Zigbee2MQTT With HomeAssistant
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/m...

#homeassistant #homeautomation #housestuff #iot #zigbee #zigbee2mqtt

0 0 0 0

So I did the dishes, checked on my property taxes, did other finance stuff, got my coffee delivery and stored it in the basement canning room, started pizza dough in the bread machine. It’s been ages since I’ve made pizza dough I hope it’s ok. #Monday #housestuff

4 0 1 0
Original post on mastodon.bentasker.co.uk

New #Blog: Using HomeAssistant To Control Solis Inverter Charge Schedules
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/c...

#automation #battery #homeassistant #homeautomation #housestuff […]

1 0 1 0
Preview
2024 Solar Performance <img class="introimage" itemprop="image" src="https://www.bentasker.co.uk/images/BlogItems/soliscloud_telegraf_plugin/solar_stats_img.jpg" style="display: none"/><div> <p><a href="https://www.bentasker.co.uk/2024/">2024</a> was our first <em>full</em> year of Solar.</p> <p>Unfortunately, it felt like we had quite a short summer compared to 2023, which will likely have pulled our numbers down a bit.</p> <p>At the beginning of 2024, I <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/solar-performance-in-2023.html">summarised 2023's performance</a>, so figured that it was worth doing again this year.</p> <p>This post is a quick summary of how our system performed over the course of the year.</p> <!-- TEASER_END --> <hr/> <h4 id="install-overview">Install Overview</h4> <p>We've got</p> <ul> <li>2 east facing arrays of panels</li> <li> <code>6 kWh</code> of raw battery capacity, provided by 3 Pylontech US2000 battery units</li> <li>A Solis 3101 inverter</li> </ul> <p>The inverter sends stats to Soliscloud which <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/pulling-soliscloud-stats-into-influxdb-with-telegraf.html">I then pull into InfluxDB with Telegraf</a> (there's a copy of the telegraf plugin <a href="https://github.com/bentasker/telegraf-plugins/tree/master/soliscloud">here</a>).</p> <hr/> <h4 id="headline-figures">Headline Figures</h4> <p>Across 2024, the headline figures are</p> <ul> <li>Energy Generated: <code>2.63 MWh</code> </li> <li>Import value of generated energy: <code>£604</code> </li> <li>Battery Charged (inc Grid charges): <code>3.00 MWh</code> </li> <li>Battery Supplied: <code>2.8 MWh</code> </li> <li>Exported: <code>264 kWh</code> </li> </ul> <p>Note: the import value is based on the price at the time that the energy was generated. We're on a variable tariff, so if it was charged into the battery prices may have been higher or lower when it was actually consumed.</p> <hr/> <h4 id="generation-timings">Generation Timings</h4> <p>I have a weather station in the garden which <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/receiving-weather-info-from-ecowitt-weather-station-and-writing-to-influxdb.html">collects</a> information.</p> <p>The average level of solar radiation was quite a bit lower than the summer of 2023 which peaked at 750 W/m2</p> <p><img alt="Graph showing solar radiation through 2024" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/solar_radiation.png"/></p> <p>The following query checks the amount of time that the panels spent active - defined as generating more than 100w:</p> <pre><code class="language-flux">from(bucket: "Systemstats") |&gt; range(start: 2024-01-01T00:00:00Z) |&gt; filter(fn: (r) =&gt; r._measurement == "solar_inverter") |&gt; filter(fn: (r) =&gt; r._field == "panel_1" or r._field == "panel_2") |&gt; aggregateWindow(every: 5m, fn: max) |&gt; group() |&gt; aggregateWindow(every: 5m, fn: sum) |&gt; filter(fn: (r) =&gt; exists r._value) |&gt; map(fn: (r) =&gt; ({r with state: if r._value &gt; 100 then "active" else "down" })) |&gt; elapsed(unit: 1s) |&gt; filter(fn: (r) =&gt; r.state == "active") |&gt; sum(column: "elapsed") </code></pre> <p>The panels were active for a total of <code>3443 hours</code> (about 143 days).</p> <p>Although we didn't have quite the peak that we had in 2023, production didn't drop off sharply until later in the year (in 2023, it dropped in October)</p> <p><img alt="Chart showing daily generation. It peaked at about 21kWh in summer and then begins to decline. It doesn't properly drop off until November" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/daily_generation.png"/></p> <hr/> <h4 id="exports">Exports</h4> <p>Exports were less sporadic than last year</p> <p><img alt="Chart showing solar exports over the course of 2024" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/exports.png"/></p> <p>We stopped exporting much earlier in the year though. </p> <p>This is because there were a far fewer <a href="https://octopus.energy/press/Saving-sessions-one-million/">Savings Sessions</a> (actually, I <em>want</em> to say there wasn't one, but maybe I've forgotten). In 2023 we exported quite a bit in November and December to take advantage of elevated unit rates during Savings Sessions. In Winter 2024, there was no incentive to do so.</p> <hr/> <h4 id="import-prices">Import Prices</h4> <p>This isn't really solar specific, but I included a graph last year to help explain elevated winter exports and see no reason not to do similar now.</p> <p><img alt="Graph of Octopus Agile prices" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/agile_prices.png"/></p> <p>December was quite a bit more expensive in 2024 than in 2023, with some horrific spikes in the max price (and I <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/will-our-hottub-thermal-cover-break-even.html#savings">accidentally ran the hottub during them</a>, eek!).</p> <hr/> <h4 id="battery-health">Battery Health</h4> <p>The battery's reported state-of-health (SOH) dropped by around 4% across the year:</p> <p><img alt="Graph showing the battery's reported SOH" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/battery_lifetime.png"/></p> <p>This is quite a significant increase in rate compared to 2023, but was expected: towards the end of 2023 I <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/reviewing-our-solar-battery-savings.html">increased the number of discharges per day</a>.</p> <p>As well as using the battery more frequently, we've benefited from being able to use <em>more</em> of the battery than last winter: In 2023, I had to increase the minimum state of charge (SOC) to 35% as the result of undervoltage alarms, but no such alarms fired this year, so I've left the setting at 20%.</p> <p>This is because, odd cold snap aside, temperatures haven't been as low so far this winter:</p> <p><img alt="Graph showing outside temperatures over the year" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/outdoor_temperature.png"/></p> <p>Our overall utilisation will have reduced a little, though, by an issue that we had at the beginning of the year with <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/our-us2000c-batteries-stopped-kicking-in-on-schedule.html">the battery not kicking in</a>.</p> <hr/> <h4 id="accumulated-usage">Accumulated Usage</h4> <p>The system has now been running for a little over 18 months.</p> <p>In that time:</p> <ul> <li>We've generated <code>4.63 MWh</code> of clean energy</li> <li>Charged <code>4.28 MWh</code> of energy into the battery (including energy taken from grid)</li> <li>Discharged <code>3.98 MWh</code> from the battery</li> <li>Exported <code>371 kWh</code> of energy to the grid</li> </ul> <p>July 2023 set a record (<code>67.9 kWh</code>) for export which has remained unbroken:</p> <p><img alt="Monthly export since May 2023" src="https://www.bentasker.co.uk/images/BlogItems/solar_review_2024/yoy_export.png"/></p> <hr/> <h4 id="changes-expected-in-2025">Changes Expected In 2025</h4> <p>This year, I will get around to implementing intelligent automated scheduling of charges/discharges because Soliscloud now have docs up on the <a href="https://oss.soliscloud.com/doc/SolisCloud%20Device%20Control%20API%20V2.0.pdf">control api</a> (in fact, I've <a href="https://projects.bentasker.co.uk/gils_projects/project/misc/soliscloud-inverter-control.html">already started</a>).</p> <p>It's likely to be a chunk of work, but getting HomeAssistant to manage dynamic charge and discharge schedules should bring both cost and efficiency benefits.</p> </div>

2024 Solar Performance
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/s...

#electricity #housestuff #solar

0 0 0 0
Preview
Is Our Hot Tub Thermal Cover Worth The Cost? <img class="introimage" itemprop="image" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/more_efficient.jpg" style="display: none"/><div> <p>This year, we bought an <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/automating-our-hottub-with-home-assistant.html">inflatable hot tub</a>, which has a 2kW heating element.</p> <p>Warming the tub wasn't too bad in summer: energy prices were lower and our solar panels supplied a good chunk of the power.</p> <p>Winter, of course, has been another matter. Not only is energy more expensive, but the lower ambient temperature means that the heater needs to kick in more frequently to help maintain even our "idle" temperature of 20°C (chosen to strike a balance between energy usage and warm up time).</p> <p>The tub isn't <em>just</em> an inflatable rubber ring, it has a thermally lined outer layer (BestWay call the material <a href="https://www.bestwaystore.co.uk/blogs/all/energysense">EnergySense</a> and claim that it increases efficiency by 40%). Similar material is used in the lid and the tub also sits on a sheet of interlocking foam tiles. </p> <p>So there's already <em>some</em> level of insulation.</p> <p>All the same, though, the tub has been consuming <em>quite a lot</em> of energy on the days that we have it warm.</p> <p>Adding a thermal cover, on the face of it, seemed like a no-brainer: </p> <pre><code class="language-text">additional insulation = reduced heat loss reduced heat loss = lower running cost </code></pre> <p>The catch though, is the <em>price</em>. Lay-z-spa, for example, want <a href="https://www.lay-z-spa.co.uk/hot-tub-thermal-covers.html">over £100</a> for even a small cover, though <a href="https://www.therange.co.uk/outdoor-living/pools-and-hot-tubs/hot-tub-covers/cleverspa-thermal-cover-for-hot-tubs/">unbranded covers</a> are cheaper.</p> <!-- TEASER_END --> <hr/> <h4 id="claimed-energy-efficiency">Claimed Energy Efficiency</h4> <p>Lay-z-spa claim that their cover can deliver <em>up to</em> 40% more efficiency, which seems bold.</p> <p>Not wanting to spend £140 on this experiment, though, I went for the non-branded option at £64.99.</p> <p>The box that it arrived in made an even bolder claim:</p> <p><img alt="Photo of the cover's box, which claims it can be up to 50% more energy efficient" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/more_efficient.jpg"/></p> <p>Presumably, in order to actually achieve that level of improvement, you'd need to have been running an un-insulated tub, with no lid, out in the wind.</p> <hr/> <h4 id="previous-energy-use">Previous Energy Use</h4> <p>The hot-tub's power supply isn't independently metered, so it's not possible to get an exact read of energy consumption. </p> <p>However, because I have the <a href="https://github.com/cdpuk/ha-bestway">HomeAssistant integration</a> set up, I do have a record of heater state, so it's possible to approximate energy usage by combining time active with the heater's rating (it has a single 2kW element):</p> <pre><code class="language-flux">import "contrib/tomhollingworth/events" from(bucket: "home_assistant/autogen") |&gt; range(start: v.timeRangeStart) |&gt; filter(fn: (r) =&gt; r._measurement == "climate.spa_thermostat" and r._field == "hvac_action_str") |&gt; events.duration(unit: 1s) |&gt; filter(fn: (r) =&gt; r._value == "heating") |&gt; map(fn: (r) =&gt; ({ r with // 2.03 is heater plus pump _value: (float(v: r.duration) / 3600.0) * 2.03 })) |&gt; aggregateWindow(every: 1d, fn: sum) </code></pre> <p>This generates a graph showing energy consumption per day</p> <p><img alt="Graph showing energy usage per day" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/usage_before.png"/></p> <p>You can see when the tub is being brought out of idle and up to temperature, consuming <em>nearly 25kWh</em> in the process. By way of comparison, our normal daily household consumption is between 20 and 30 kWh so heating the tub pretty much <em>doubles</em> our usage.</p> <p>The usage calculations can be combined with <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/writing-octopus-pricing-and-usage-into-influxdb-with-telegraf.html">Octopus pricing information</a> to arrive at a cost per day:</p> <pre><code class="language-flux">import "contrib/tomhollingworth/events" usage = from(bucket: "hottub") |&gt; range(start: v.timeRangeStart) |&gt; filter(fn: (r) =&gt; r._measurement == "climate.spa_thermostat" and r._field == "hvac_action_str") |&gt; events.duration(unit: 1s) |&gt; filter(fn: (r) =&gt; r._value == "heating") |&gt; map(fn: (r) =&gt; ({ r with // 2.03 is heater plus pump _value: (float(v: r.duration) / 3600.0) * 2.03 })) |&gt; aggregateWindow(every: 30m, fn: sum) pricing = from(bucket: "Systemstats") |&gt; range(start: v.timeRangeStart) |&gt; filter(fn: (r) =&gt; r._measurement == "octopus_pricing") |&gt; filter(fn: (r) =&gt; r.charge_type == "usage-charge" and r.tariff_direction != "EXPORT") |&gt; filter(fn: (r) =&gt; r._field == "cost_inc_vat") |&gt; aggregateWindow(every: 30m, fn: mean) join(tables: {t1: usage, t2: pricing}, on: ["_time"]) // Calculate cost based on grid price at the time |&gt; map(fn: (r) =&gt;({ _time: r._time, _value: (r._value_t1 * r._value_t2) / 100.0, _field: "usage_cost" })) |&gt; aggregateWindow(every: 1d, fn: sum) </code></pre> <p><img alt="Daily energy cost of running the hottub" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/cost.png"/></p> <p>I regretted creating this graph almost as soon as I saw it... <em>ouch</em>.</p> <p>Unfortunately, this graph isn't <em>particularly</em> useful for comparison: we're on a variable price tariff, so it would be misleading to try and draw conclusions from the total spent because the underlying prices can vary quite significantly.</p> <hr/> <h4 id="rate-of-temperature-change">Rate of Temperature Change</h4> <p>Assuming that it helps at all, the additional insulation provided by a cover helps by reducing the rate that heat escapes the tub.</p> <p>So the other thing that I needed to look at was the average hourly change in temperature.</p> <p>Decreases:</p> <pre><code class="language-sql">SELECT mean("temperature_change") FROM ( SELECT derivative(mean("current_temperature"), 1h) AS "temperature_change" FROM "hottub"."autogen"."climate.spa_thermostat" WHERE time &gt; now() - 30d GROUP BY time(1h) FILL(null) ) WHERE "temperature_change" &lt; 0 GROUP BY time(1h) </code></pre> <p><img alt="Graph showing the average hourly drop in temperature across the prior 30 days" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/temperature_decrease.png"/></p> <p>The graph for temperature increases is generated using much the same query, just with the final conditional swapped</p> <pre><code class="language-sql">SELECT mean("temperature_change") FROM ( SELECT derivative(mean("current_temperature"), 1h) AS "temperature_change" FROM "hottub"."autogen"."climate.spa_thermostat" WHERE time &gt; now() - 30d GROUP BY time(1h) FILL(null) ) WHERE "temperature_change" &gt; 0 GROUP BY time(1h) </code></pre> <p><img alt="Graph showing increases over time" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/temperature_increase.png"/></p> <p>Reducing each of these to aggregates gets us the following figures:</p> <table> <thead><tr> <th></th> <th><strong>Min °C</strong></th> <th><strong>Mean °C</strong></th> <th><strong>Max °C</strong></th> </tr></thead> <tbody> <tr> <td><strong>Drop</strong></td> <td>0.0161</td> <td>0.451</td> <td>1.5</td> </tr> <tr> <td><strong>Rise</strong></td> <td>0.0185</td> <td>1.04</td> <td>2.54</td> </tr> </tbody> </table> <hr/> <h4 id="fitting-the-cover">Fitting The Cover</h4> <p>The cover seems to be pretty good quality - in terms of material and thickness, it feels like a moderately good sleeping bag.</p> <p>It's got a rectangular panel which can be unzipped to allow for connections to an external pump:</p> <p><img alt="Photo of the hot-tub with cover on, there's a rectangular gap to allow for pipes to the pump and heat unit" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/hot_tub_with_cover.jpg"/></p> <p>One thing that I was concerned about was puddling. </p> <p>The rain sometimes blows in and collects on the tub's lid - that's annoying but quite easily dealt with when lifting a flat rigid lid. The new cover is more like a blanket though, and I didn't fancy getting splashed with cold, manky water when trying to take it off.</p> <p>It's only the outermost edge which is really exposed to the rain, so I placed a plastic storage box just back from it before placing the cover on in the hope that most of the rain would then run down the side of it and off the tub.</p> <p>This does mean that there will have been a little more air under the cover than if it were flat against the lid.</p> <hr/> <h4 id="impact-cooling">Impact: Cooling</h4> <p>The impact on the rate of cooling is quite visible in the temperature graph:</p> <p><img alt="Graph showing water termperature. Once the cover is fitted, the rate of cooling is definitely slower" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/decrease_with_cover.png"/></p> <p>Before the cover was fitted, the tub would often reach the idle temperature of 20°C. The little spikes indicate that the thermostat was sometimes clicking back on to help maintain that temperature.</p> <p>The cool down period after the cover was fitted, though, didn't reach idle, so the next heating period started from 23°C rather than 20°C</p> <p>Because it takes a known amount of energy to heat 1L of water, we can work out roughly what energy this might save:</p> <pre><code>1_litre = 3C x 4.18 kilojoule = 12.54kj 600l = 12.54kj * 600l = 7524kj kwh = 7524 kj / 3600 = 2.09kWh </code></pre> <p>So, that extra few degrees <em>should</em> have shaved about <code>2 kWh</code> off the heater's energy usage. That's not a <em>huge</em> amount, but it's still about 8% of the first day's warm.</p> <p>If we overlay the decrease graph onto water temperature we can see that the rate of decrease <em>was</em> generally more gentle with the cover, although there's not <em>much</em> change when the water is warmest.</p> <p><img alt="Graph showing rate of decrease, overlaid onto water termperatures. Decreases don't spike as much once the cover has been fitted." src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/decrease_v_temp.png"/></p> <p>So, although there is an impact on the rate of cooling, it's quite modest and only really occurs when the water's cooler.</p> <hr/> <h4 id="impact-warming">Impact: Warming</h4> <p>Our best potential for financial savings occur when the tub is being warmed - it's all very well it cooling more slowly, but I'm not going to be climbing into the thing whether it's 20°C or 23°C. An increased rate of warming, though, would mean that we need to burn less energy to warm the tub back up.</p> <p>But, have these savings been realised?</p> <p>If we overlay rate of temperature increase onto the temperature graph:</p> <p><img alt="Graph with rate of increase overlaid against water temperature" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/increase_v_temp.png"/></p> <p>There's very little difference at all.</p> <p>In fact, if we swap the overlay to show maximum increase</p> <pre><code class="language-sql">SELECT max("temperature_change") FROM ( SELECT derivative(max("current_temperature"), 1h) AS "temperature_change" FROM "hottub"."autogen"."climate.spa_thermostat" WHERE time &gt; now() - 30d GROUP BY time(1h) FILL(null) ) WHERE "temperature_change" &gt; 0 GROUP BY time(1h) </code></pre> <p>We can see that, with the exception of one point, there is <em>no difference</em>:</p> <p><img alt="Graph showing the maximum hourly temperature increase" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/temp_vs_max_increase.png"/></p> <p>That one jump of 3°C is <em>probably</em> anomalous and the result of reduced flow through the filter (more on that below).</p> <hr/> <h4 id="impact-energy-consumption">Impact: Energy Consumption</h4> <p>The entire aim of using a cover is to reduce energy consumption. The tub may not appear to have been warming any faster, but perhaps there are some marginal energy gains?</p> <p>If there are, they are <em>extremely</em> minimal:</p> <p><img alt="Graph showing daily energy consumption of the tub" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/energy_usage.png"/></p> <p>In fact, if we look at a graph showing heater state we can see that there really isn't that much difference in time spent active when warming back up:</p> <p><img alt="Chart showing the state of the heater over the last 30 days. The pattern is much the same before and after the cover was fitted" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/heater_state.png"/></p> <p>Note: the random seeming spikes are the heater clicking on as a result of <a href="https://www.bentasker.co.uk/posts/blog/house-stuff/automating-our-hottub-with-home-assistant.html#automation">my automation for Plunge Pricing</a>.</p> <p>However, if we look at the energy consumed on days where the heater simply has to maintain 38°C, there <em>does</em> appear to be a reduction:</p> <p><img alt="Graph showing energy usage when the heater simply has to maintain 38c rather than reach it" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/idling_usage_extra_day.png"/></p> <p>Before the cover was added, the average daily usage was <code>8.32 kWh</code>. Afterwards, it was <code>5.94 kWh</code>, yielding a saving of <code>2.38 kWh</code> a day. Of course, we're only in that maintenance state for 2 days, so the weekly saving is <code>4.76 kWh</code> a week.</p> <hr/> <h4 id="savings">Savings</h4> <p>We should try and convert that saving into money so that we can work out when savings would break even with the cover's purchase price.</p> <p>To do that, we need to know what energy prices look like when the tub is heating:</p> <pre><code class="language-flux">import "contrib/tomhollingworth/events" usage = from(bucket: "hottub") |&gt; range(start: v.timeRangeStart) |&gt; filter(fn: (r) =&gt; r._measurement == "climate.spa_thermostat" and r._field == "hvac_action_str") |&gt; events.duration(unit: 1s) |&gt; filter(fn: (r) =&gt; r._value == "heating") |&gt; map(fn: (r) =&gt; ({ r with // 2.03 is heater plus pump _value: (float(v: r.duration) / 3600.0) * 2.03 })) |&gt; aggregateWindow(every: 30m, fn: sum) pricing = from(bucket: "Systemstats") |&gt; range(start: v.timeRangeStart) |&gt; filter(fn: (r) =&gt; r._measurement == "octopus_pricing") |&gt; filter(fn: (r) =&gt; r.charge_type == "usage-charge" and r.tariff_direction != "EXPORT") |&gt; filter(fn: (r) =&gt; r._field == "cost_inc_vat") |&gt; aggregateWindow(every: 30m, fn: mean) // Join the two so we only get prices at times // that the heater was active join(tables: {t1: usage, t2: pricing}, on: ["_time"]) |&gt; filter(fn: (r) =&gt; exists r._value_t1) |&gt; map(fn: (r) =&gt;({ _time: r._time, _value: r._value_t2, _field: "unit_cost" })) // Exclude plunge pricing |&gt; filter(fn: (r) =&gt; r._value &gt; 0) </code></pre> <p>By bolting different aggregates onto the end, we can arrive at the following unit prices and potential savings (based on <code>4.76 kWh</code> a week)</p> <table> <thead><tr> <th></th> <th><strong>Price</strong></th> <th>Savings</th> </tr></thead> <tbody> <tr> <td><strong>Min</strong></td> <td>0.221</td> <td>1.05196</td> </tr> <tr> <td><strong>Mean</strong></td> <td>20.7</td> <td>98.532</td> </tr> <tr> <td><strong>Max</strong></td> <td>94.8</td> <td>451.248</td> </tr> </tbody> </table> <p>Note: that max value is horrifying, I should probably set up an automation to turn the heater off when prices are that high.</p> <p>Assuming that that's all we manage to save, the payback time for a £64.99 cover is therefore</p> <table> <thead><tr> <th></th> <th></th> </tr></thead> <tbody> <tr> <td><strong>Min</strong></td> <td>118 Years</td> </tr> <tr> <td><strong>Mean</strong></td> <td>1 Year 3 months</td> </tr> <tr> <td><strong>Max</strong></td> <td>14 Weeks</td> </tr> </tbody> </table> <p>Prices either side of a plunge pricing session are generally quite low, so I wondered whether they were skewing the mean price and re-ran the query, excluding any price below <code>£0.10/kWh</code>. Unfortunately, the mean only shifted to <code>£0.23/kWh</code> which would still give a payback time of 1 year 1 month.</p> <p>Although that's not horrific, it's far from a given that the cover will survive being exposed to the elements for long enough. The real break-even period will <em>probably</em> be longer too as the average price is lower in Summer.</p> <hr/> <h4 id="external-influences">External Influences</h4> <p>There are a few things which make direct comparison harder and less reliable.</p> <h5 id="the-weather">The weather</h5> <p>The hot tub is sat outside and the average ambient temperature has been lower over the last couple of weeks.</p> <p><img alt="Graph showing the temperature measured by my weather station. Although it hasn't dipped as low, the average temperature has been lower" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/weather_temperature.png"/></p> <p>However, whilst this will have increased the rate at which we lose heat, it <em>shouldn't</em> be enough to almost completely offset the gains of having a cover - I'd expect most of the impact to occur at peak and at idle.</p> <h5 id="the-filter">The Filter</h5> <p>The tub's pump pulls water into the heater via a pleated paper filter. Over time, that filter scales up and starts to clog, reducing the rate at which water is pulled through.</p> <p>This can have quite an impact on heat circulation: it reduces the amount of pressure behind the newly warmed water and so doesn't push it as far into the tub. This reduced rate of mixing can lead to cool spots forming.</p> <h5 id="usage">Usage</h5> <p>Unsurprisingly, taking the lid off and exposing the surface of a 38°C body of water to 5°C air allows for an increased rate of heat loss.</p> <p>It's not as visible in graphs covering a week, but if we compare consecutive days, we can see the heater kicking in:</p> <p><img alt="Graph showing heater state over 2 days. I had a quick dip around 10pm Friday and the heater kicked in part way through" src="https://www.bentasker.co.uk/images/BlogItems/hottub_cover/heater_detailed.png"/></p> <p>As a result of the tub being used, the heater ran for about 40 minutes, causing about <code>1.33 kWh</code> of additional consumption.</p> <h5 id="the-inconvenience">The inconvenience</h5> <p>So far, I've been good at putting the cover back on. However, it really is quite inconvenient to fit properly. That inconvenience is only really amplified by the fact that hot tubs need regular maintenance (water quality checks, dropping new chlorine tablets in etc). </p> <p>Given how often the cover needs to be disturbed, it'd be quite easy to start rationalising not putting it back on "because we'll probably use the tub tomorrow anyway".</p> <hr/> <h4 id="conclusion">Conclusion</h4> <p>It looks unlikely that the cover will lead to enough savings to offset it's purchase price in a meaningful time frame.</p> <p>It bears noting, though, that I <strong>did</strong> cheap out. It is quite possible that a Lay-z-spa cover is more efficient, but I think it's unlikely that it would change the outcome: given the additional cost, it'd need to be <em>twice</em> as efficient just to break even with the cover that I did buy.</p> <p>I don't think that this is necessarily a reflection of the efficiency of thermal covers as a whole, so much as a reflection of just how efficient our tub <em>already was</em>: adding the additional layer only seems to have saved us about 3°C of cooling <em>over the course of a week</em>, far less than I was expecting.</p> <p>I <em>suspect</em> that the cover might get a less well insulated tub toward the level of efficiency that ours already achieves. The name "EnergySense" might grate on my nerves, but it seems it probably does do what they claim.</p> <p>During warmup, regardless of the level of insulation, the heater ultimately has to warm 600 litres of water from 20°C to 38°C, which is always going to demand a reasonable chunk of energy.</p> <p>Although not huge, there <em>do</em> seem to be savings on the days that the tub is simply maintaining temperatures. At a little over a year, the break-even point is a little further out than I'd like, but not unachievable <em>especially</em> as we (unsurprisingly) see better savings on the days where the tub doesn't end up being used.</p> </div>

New #Blog: Is Our Hot Tub Thermal Cover Worth The Cost?
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/w...

#analysis #electrical #hottub #housestuff #influxql

0 0 0 0
Preview
Dog Proofing The Bottom Of A Chain-Link Fence <img class="introimage" itemprop="image" src="https://www.bentasker.co.uk/images/BlogItems/bonnie_2_months/daschshund_u_fuckin_wot.jpg" style="display: none"/><div> <p>Recently, we've had an issue with one of the dogs escaping through a boundary fence.</p> <p>The fence in question is chainlink, within a hedgerow and was present before we moved here. Because it had been installed as dog proofing (the previous owners had a little Daxie), the bottom of the fence was, quite sensibly, buried.</p> <p>However, the smaller of our dogs - Pepper - appears to have figured out that, because chain link is stretchy, she only needs to dig a little bit of the bottom out before she's able to squeeze under. </p> <p>Unfortunately, this revelation seems to have been motivated by the smelly delights left by a neighbourhood cat on the other side of the fence (yeuch).</p> <p>This post talks about dog-proofing the fence and the ground around it as well as discouraging the cat from using that area.</p> <!-- TEASER_END --> <hr/> <h4 id="the-options">The Options</h4> <p>The fence being in the middle of a hedge immediately ruled a few things out</p> <ul> <li>Can't concrete around the base of the fence, because it'd kill the hedge</li> <li>Can't run chicken-wire in an L shape to prevent digging: there are roots and trunks in the way</li> <li>Can't switch the fence from chainlink to panels: we'd never get the panels into the hedge.</li> </ul> <p>Erecting a new fence in front of the hedge <em>could</em> have worked, but would have required a <strong>lot</strong> of time and effort - it's certainly not something that could be done quickly or cheaply.</p> <p>Instead, the base of the existing fence needed to be reinforced - making the chainlink less amenable to being stretched (and harder to dig up in the first place).</p> <hr/> <h4 id="the-plan">The Plan</h4> <p>The plan was simple:</p> <ul> <li>Dig the bottom of the fence up</li> <li>Staple it onto some timber</li> <li>Bury the timber</li> </ul> <p>Stapling each of the links to the wood makes the fence less able to stretch and helps to make it harder to squeeze under - a large part of the timber would need digging out before the bottom of the fence would lift at all.</p> <hr/> <h4 id="putting-it-into-practice">Putting it into practice</h4> <p>Although the plan was simple, <em>actually doing it</em> turned out to be fairly awful: I had to work on hands and knees, sticking my head into the hedge so that I could see what my hands were doing.</p> <p>Unsurprisingly, I couldn't manoeuvre a 2 metre length of 2x4 into place, so I had to chop the timber down and work in sections.</p> <p>After using a trowel to dig the fence up, I laid the 2x4 on it's front and used an electric nailgun to staple the links onto the wood.</p> <p>Then, I pushed the wood backwards and dug a small trench, before part burying the beam in it:</p> <p><img alt="A length of timber is half buried. There's a green chainlink fence running down the back of it" src="https://www.bentasker.co.uk/images/BlogItems/dogproofing_fence/fence_with_timber2.jpg"/></p> <p>Because it's so sheltered, the earth at the bottom of the hedge is very dry and sandy - the only real obstacle to digging is roots.</p> <p>So, to help make it harder for the dog to dig the timbers back out (or worse, dig under them) I made up some thick wooden pegs and bashed them in on either side (making it harder to tip the timber in either direction) and placed bricks along the front of the timber</p> <p><img alt="The wood is now held in place by some wooden pegs and bricks" src="https://www.bentasker.co.uk/images/BlogItems/dogproofing_fence/fence_with_bricks.jpg"/></p> <p>As soon as I can get some (it's <em>not exactly</em> the best time of year for hardcore deliveries), I'll cover the area with stones to discourage digging further.</p> <hr/> <h4 id="discouraging-cats">Discouraging Cats</h4> <p>None of this work, though, dealt with the <em>true</em> root cause: the dog was only really trying to get through the fence because cats were burying smelly "treats" in the soil on the other side of it.</p> <p>Realistically, as long as those continued to appear, the dog was going to continue to be motivated to try and get past the wirey inconvenience.</p> <p>Unfortunately, some people are <em>complete cunts</em> and try to keep cats away by doing things like putting out <a href="https://old.reddit.com/r/iamatotalpieceofshit/comments/og2wjm/putting_glass_on_the_fence_to_stop_cats_getting/">broken glass</a> (or worse, <a href="https://www.rspca.org.uk/adviceandwelfare/pets/cats/health/poisoning/antifreeze">anti-freeze</a>). It doesn't matter how special you think your petunias are, that's <em>never</em> an acceptable solution.</p> <p>The aim here wasn't to harm (or even scare) the cats - I simply needed them to go <em>somewhere else</em>.</p> <p>Cats have a strong sense of smell and amongst other things don't particularly like</p> <ul> <li>Citrus</li> <li>Coffee</li> <li>Rosemary</li> </ul> <p>By luck, I trimmed a heavily overgrown rosemary bush back a week or two ago, so had plenty of that kicking around.</p> <p>Along with some hedge trimmings (to help bulk it out), I pushed the rosemary through a mulcher and then shovelled it along the other side of the fence.</p> <p>I'll likely top it up every few days with used coffee grounds.</p> <p>Dogs aren't particularly fond of those smells either, so the added benefit is that it <em>should</em> also help keep them away from that area in the first place.</p> </div>

Dog Proofing The Bottom Of A Chain-Link Fence
Author: Ben Tasker

www.bentasker.co.uk/posts/blog/house-stuff/p...

#dogs #garden #housestuff

1 1 1 0

Shark StainStriker portable carpet cleaner is really great. It has an onboard mechanism to clean the tool. Separate dirty & clean water tanks. Took out a pet stain easily. Lightweight.

No, this isn’t an ad—I bought it with my own money. I’m just impressed by it so thought I’d share.
#housestuff

4 0 0 0
Replacement cabinet hinges

Replacement cabinet hinges

When the old slow close hinges break (the left), they won't stay closed. Cabinet door side was a direct fit, but the cabinet side I did have to drill the lower mounting hole as the spread of that mount was maybe an 1/8" wider. Used the top hole & drilled a new lower hole, now all good. #HouseStuff

0 0 1 0