So after building a reliable enough software for my open433 board, I wanted to make add my project as a platform for home assistant.
So after building a reliable enough software for my open433 board, I wanted to make add my project as a platform for home assistant. However, I found official documentation by homeassistant.io very shitty when you don't know the inner workings of the platform so here's my take on it:
To create a home assistant component you need to create a folder named custom_components folder in the home assistant configuration directory (same one as the configuration.yaml) then in this older you need to create a folder with the name of your integration.
In this folder create a file name manifest.json this is where we will be declaring information about you integration:
1{
2 "domain": "open433",
3 "name": "Open433 board",
4 "documentation": "https://github.com/TheStaticTurtle/Open433",
5 "requirements": ["pyserial==3.4"],
6 "codeowners": ["@TheStaticTurtle"]
7}
In here you specify the domain of your integration then the description the documentation of the project the requirements needed by you program (in my case pyserial) and then the GitHub usernames of who made the code
Then in the same folder create a file named init.py in this file you will need to declare the configuration needed by your integration. So first import some constants / libraries:
1import logging
2from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
3import homeassistant.helpers.config_validation as cv
4import voluptuous as vol
5import threading
6from . import rcswitch
in my case I have the library that I made in the same folder (named rcswitch.py). Then you need to declare some stuff like the domain (the name of your integration) , the name of the parameters used by the configuration and finally the configuration schema:
1_LOGGER = logging.getLogger(__name__)
2
3DOMAIN = "open433"
4
5CONF_COMPORT = "port"
6CONF_COMSPEED = "speed"
7
8REQ_LOCK = threading.Lock()
9CONFIG_SCHEMA = vol.Schema(
10 {
11 DOMAIN: vol.Schema({
12 vol.Required(CONF_COMPORT): cv.string,
13 vol.Optional(CONF_COMSPEED, default=9600): cv.positive_int,
14 })
15 },
16 extra=vol.ALLOW_EXTRA,
17)
So for me the configuration could look like this:
1open433:
2 port: COM3
That's it for declaring the constants now you need to declare the actual setup function my one looks like this:
1def setup(hass, config):
2 conf = config[DOMAIN]
3 comport = conf.get(CONF_COMPORT)
4 comspeed = conf.get(CONF_COMSPEED)
5
6 rf = rcswitch.RCSwitch(comport, speed=comspeed)
7 rf.libWaitForAck(True, timeout=1)
8
9 def cleanup(event):
10 rf.cleanup()
11
12 def prepare(event):
13 rf.prepare()
14 rf.startReceivingThread()
15 hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup)
16
17 hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare)
18 hass.data[DOMAIN] = rf
19
20 return True
To break it down first we get the configuration from home assistant config for the current domain (line 2) then we retrieve the config (line3-4). Then specific to me I initialize my library (line6-7) then we define function for home assistant to execute while it start stops (line9-17) and then I store the instance of my library into home assistant data for the specific domain.
And that all for the __init__.py
After all this you need to add the component for the platform as my project controls ac rf switches I wanted to make a switch component, to do that simply create a switch.py in the same folder
1import logging
2
3import voluptuous as vol
4
5from homeassistant.components.switch import SwitchEntity, PLATFORM_SCHEMA
6from homeassistant.const import CONF_NAME, CONF_SWITCHES
7import homeassistant.helpers.config_validation as cv
8from . import DOMAIN, REQ_LOCK, rcswitch
9
10_LOGGER = logging.getLogger(__name__)
11
12CONF_CODE_OFF = "code_off"
13CONF_CODE_ON = "code_on"
14CONF_PROTOCOL = "protocol"
15CONF_LENGTH = "length"
16CONF_SIGNAL_REPETITIONS = "signal_repetitions"
17CONF_ENABLE_RECEIVE = "enable_receive"
18
19SWITCH_SCHEMA = vol.Schema(
20 {
21 vol.Required(CONF_CODE_OFF): vol.All(cv.ensure_list_csv, [cv.positive_int]),
22 vol.Required(CONF_CODE_ON): vol.All(cv.ensure_list_csv, [cv.positive_int]),
23 vol.Optional(CONF_LENGTH, default=32): cv.positive_int,
24 vol.Optional(CONF_SIGNAL_REPETITIONS, default=15): cv.positive_int,
25 vol.Optional(CONF_PROTOCOL, default=2): cv.positive_int,
26 vol.Optional(CONF_ENABLE_RECEIVE, default=False): cv.boolean,
27 }
28)
29
30PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
31 {
32 vol.Required(CONF_SWITCHES): vol.Schema({cv.string: SWITCH_SCHEMA}),
33 }
34)
The start is very similar to the init file you import everything and define what the configuration will look like. This what my configuration looks like (the two first line are home assistant specific)
1switch:
2 - platform: open433
3 switches:
4 switchA:
5 code_on: 2389577216
6 code_off: 2171473408
7 protocol: 2
8 length: 32
9 signal_repetitions: 5
10 enable_receive: true
Then you need to set up the platform
1def setup_platform(hass, config, add_entities, discovery_info=None):
2 rf = hass.data[DOMAIN]
3
4 switches = config.get(CONF_SWITCHES)
5 devices = []
6 for dev_name, properties in switches.items():
7 devices.append(
8 Open433Switch(
9 properties.get(CONF_NAME, dev_name),
10 rf,
11 properties.get(CONF_PROTOCOL),
12 properties.get(CONF_LENGTH),
13 properties.get(CONF_SIGNAL_REPETITIONS),
14 properties.get(CONF_CODE_ON),
15 properties.get(CONF_CODE_OFF),
16 properties.get(CONF_ENABLE_RECEIVE),
17 )
18 )
19
20 add_entities(devices)
First I retrieve the rf instance that was stored earlier in the init, get the array of switches in the configuration iterate over them and then declare each device (the Open433Switch class) and tell home assistant about my entities. So about he Open433Switch:
1class Open433Switch(SwitchEntity):
2 def __init__(self, name, rf, protocol, length, signal_repetitions, code_on, code_off,enable_rx):
3 self._name = name
4 self._state = False
5 self._rf = rf
6 self._protocol = protocol
7 self._length = length
8 self._code_on = code_on
9 self._code_off = code_off
10 self._signal_repetitions = signal_repetitions
11 if enable_rx:
12 self._rf.addIncomingPacketListener(self._incoming)
13
14 def _incoming(self, packet):
15 if isinstance(packet, rcswitch.packets.ReceivedSignal):
16 if packet.length == self._length and packet.protocol == self._protocol:
17 if packet.decimal in self._code_on:
18 self._state = True
19 self.schedule_update_ha_state()
20 if packet.decimal in self._code_off:
21 self._state = False
22 self.schedule_update_ha_state()
23
24 @property
25 def should_poll(self):
26 return False
27
28 @property
29 def name(self):
30 return self._name
31
32 @property
33 def is_on(self):
34 return self._state
35
36 def _send_code(self, code_list, protocol, length):
37 with REQ_LOCK:
38 self._rf.setRepeatTransmit(self._signal_repetitions)
39 for code in code_list:
40 packet = rcswitch.packets.SendDecimal(value=code, length=length, protocol=protocol, delay=700)
41 self._rf.send(packet)
42 return True
43
44 def turn_on(self, **kwargs):
45 if self._send_code(self._code_on, self._protocol, self._length):
46 self._state = True
47 self.schedule_update_ha_state()
48
49 def turn_off(self, **kwargs):
50 if self._send_code(self._code_off, self._protocol, self._length):
51 self._state = False
52 self.schedule_update_ha_state()
Basically in the class the thing you need to worry about is the Open433Switch() that is expanding the SwitchEntity class, the should_poll propriety, the name propriety, the is_on propriety, the turn_on function and the turn_off function.
These function/ proprieties executes / get polled at different time. Example when you change the status in the dashboard the turn_on / turn_off function get executed. When the code executes self.schedule_update_ha_state() the proprieties get polled
And that's the basics
Want to chat about this article? Just post a message down here. Chat is powered by giscus and all discussions can be found here: TheStaticTurtle/blog-comments