My house knows when people are using the wifi, and keeps the inverter on so that the satellite internet is powered up, unless the battery is too low. When nobody is using the wifi, the inverter turns off, except when it's needed to power the fridge.
Sounds a little complicated, doesn't it? The code to automate that using my reactive-banana-automation library is almost shorter than the English description, and certianly clearer.
inverterPowerChange :: Sensors t -> MomentAutomation (Behavior (Maybe PowerChange))
inverterPowerChange sensors = do
lowpower <- lowpowerMode sensors
fridgepowerchange <- fridgePowerChange sensors
wifiusers <- numWifiUsers sensors
return $ react <$> lowpower <*> fridgepowerchange <*> wifiusers
where
react lowpower fridgepowerchange wifiusers
| lowpower = Just PowerOff
| wifiusers > 0 = Just PowerOn
| otherwise = fridgepowerchange
Of course, there are complexities under the hood, like where does
numWifiUsers
come from? (It uses inotify to detect changes to the
DHCP leases file, and tracks when leases expire.) I'm up to 1200 lines of
custom code for my house, only 170 lines of which are control code like
the above.
But that code is the core, and it's where most of the bugs would be. The goal is to avoid most of the bugs by using FRP and Haskell the way I have, and the rest by testing.
For testing, I'm using doctest to embed test cases along with the FRP code. I designed reactive-banana-automation to work well with this style of testing. For example, here's how it determines when the house needs to be in low power mode, including the tests:
-- | Start by assuming we're not in low power mode, to avoid
-- entering it before batteryVoltage is available.
--
-- If batteryVoltage continues to be unavailable, enter low power mode for
-- safety.
--
-- >>> runner <- observeAutomation (runInverterUnless lowpowerMode) (mkSensors (pure ()))
-- >>> runner $ \sensors -> gotEvent (dhcpClients sensors) []
-- []
-- >>> runner $ \sensors -> sensorUnavailable (batteryVoltage sensors)
-- [InverterPower PowerOff]
-- >>> runner $ \sensors -> batteryVoltage sensors =: Volts 25
-- [InverterPower PowerOn]
-- >>> runner $ \sensors -> batteryVoltage sensors =: Volts 20
-- [InverterPower PowerOff]
-- >>> runner $ \sensors -> batteryVoltage sensors =: Volts 25
-- [InverterPower PowerOn]
-- >>> runner $ \sensors -> sensorUnavailable (batteryVoltage sensors)
-- [InverterPower PowerOff]
lowpowerMode :: Sensors t -> MomentAutomation (Behavior Bool)
lowpowerMode sensors = automationStepper False
=<< fmap calc <$> getEventFrom (batteryVoltage sensors)
where
-- Below 24.0 (really 23.5 or so) is danger zone for lead acid.
calc (Sensed v) = v < Volts 24.1
calc SensorUnavailable = True
The sensor data is available over http, so I can run this controller code in test mode, on my laptop, and observe how it reacts to real-world circumstances.
joey@darkstar:~/src/house>./controller test
InverterPower PowerOn
FridgeRelay PowerOff
Previously: my haskell controlled offgrid fridge
Imagine something really cool, like a fridge connected to a powerwall, powered entirely by solar panels. What could be cooler than that?
How about a fridge powered entirely by solar panels without the powerwall? Zero battery use, and yet it still preserves your food.
That's much cooler, because batteries, even hyped ones like the powerwall, are expensive and innefficient and have limited cycles. Solar panels are cheap and efficient now. With enough solar panels that the fridge has power to cool down most days (even cloudy days), and a smart enough control system, the fridge itself becomes the battery -- a cold battery.
I'm live coding my fridge, with that goal in mind. You can follow along in this design thread on secure scuttlebutt, and my git commits, and you can watch real-time data from my fridge.
Over the past two days, which were not especially sunny, my 1 kilowatt of solar panels has managed to cool the fridge down close to standard fridge temperatures. The temperature remains steady overnight thanks to added thermal mass in the fridge. My food seems safe in it, despite it being powered off for 14 hours each night.
(Numbers in this graph are running higher than the actual temps of food in the fridge, for reasons explained in the scuttlebutt thread.)
Of course, the longterm viability of a fridge that never draws from a battery is TBD; I'll know within a year if it works for me.
I've written about the coding side of this project before, in my haskell controlled offgrid fridge. The reactive-banana-automation library is working well in this application. My AIMS inverter control board and easy-peasy-devicetree-squeezy were other groundwork for this project.