Skip to content
88 changes: 62 additions & 26 deletions grobro/ha/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def __init__(self, mqtt_config: model.MQTTConfig):
self._client.connect(mqtt_config.host, mqtt_config.port, 60)

# Subscriptions
for cmd_type in ["number", "button", "switch", "config"]:
for cmd_type in ["number", "time", "button", "switch", "config"]:
for action in ["set", "read"]:
topic = f"{HA_BASE_TOPIC}/{cmd_type}/grobro/+/+/{action}"
self._client.subscribe(topic)
Expand Down Expand Up @@ -373,7 +373,7 @@ def __on_connect(self, client, userdata, flags, reason_code, properties):

def __on_message(self, client, userdata, msg: mqtt.MQTTMessage):
parts = msg.topic.removeprefix(f"{HA_BASE_TOPIC}/").split("/")
if len(parts) != 5 or parts[0] not in {"number", "button", "switch", "config"}:
if len(parts) != 5 or parts[0] not in {"number", "time", "button", "switch", "config"}:
return
cmd_type, _, device_id, cmd_name, action = parts

Expand All @@ -387,67 +387,103 @@ def __on_message(self, client, userdata, msg: mqtt.MQTTMessage):
# Buttons
if cmd_type == "button":
if cmd_name == "read_all":
# Send all modbus reads first
# Send all modbus reads first
for name, register in known_registers.holding_registers.items():
if name.startswith("slot"):
try:
if int(name[4]) > MAX_SLOTS:
continue
except ValueError:
continue

pos = register.growatt.position
self.on_command(make_modbus_command(
device_id,
GrowattModbusFunction.READ_SINGLE_REGISTER,
pos.register_no,
))
self.on_command(
make_modbus_command(
device_id,
GrowattModbusFunction.READ_SINGLE_REGISTER,
pos.register_no,
)
)

# Queue config reads
if self.on_config_read:
with self._config_read_lock:
q = self._config_read_queues.setdefault(device_id, deque())

for cfg in known_registers.config_registers.values():
q.append(cfg.growatt.register_no)

# give the datalogger time to answer modbus reads
Timer(
3.0, # small delay is enough
3.0,
self.__kickoff_next_config_read,
args=(device_id,),
).start()

return

if action == "read":
pos = known_registers.holding_registers[cmd_name].growatt.position
self.on_command(make_modbus_command(
device_id, GrowattModbusFunction.READ_SINGLE_REGISTER, pos.register_no
))

self.on_command(
make_modbus_command(
device_id,
GrowattModbusFunction.READ_SINGLE_REGISTER,
pos.register_no,
)
)

return

# Number / Switch
if cmd_type in {"number", "switch"} and action == "set":
raw_value = msg.payload.decode()
# Number / Switch / Time
if cmd_type in {"number", "switch", "time"} and action == "set":
raw_value = msg.payload.decode().strip()

if cmd_type == "switch":
parsed_value = 1 if raw_value.upper() == "ON" else 0
elif "_start_time" in cmd_name or "_end_time" in cmd_name:
hour, minute = divmod(int(raw_value), 100)

elif cmd_type == "time":
hour, minute = map(int, raw_value.split(":")[:2])
parsed_value = (hour * 256) + minute

else:
parsed_value = int(raw_value)

pos = known_registers.holding_registers[cmd_name].growatt.position
LOG.debug("Setting %s register %s to value %s", cmd_name, pos.register_no, parsed_value)

LOG.debug(
"Setting %s register %s to value %s",
cmd_name,
pos.register_no,
parsed_value,
)

# write
self.on_command(make_modbus_command(
device_id, GrowattModbusFunction.PRESET_SINGLE_REGISTER, pos.register_no, parsed_value
))
self.on_command(
make_modbus_command(
device_id,
GrowattModbusFunction.PRESET_SINGLE_REGISTER,
pos.register_no,
parsed_value,
)
)

# read-after-write
LOG.debug("Triggering read-after-write for Command %s register %s", cmd_name, pos.register_no)
self.on_command(make_modbus_command(
device_id, GrowattModbusFunction.READ_SINGLE_REGISTER, pos.register_no
))
LOG.debug(
"Triggering read-after-write for Command %s register %s",
cmd_name,
pos.register_no,
)

self.on_command(
make_modbus_command(
device_id,
GrowattModbusFunction.READ_SINGLE_REGISTER,
pos.register_no,
)
)

return

# Config
if parts[0] == "config" and action == "set":
Expand Down
92 changes: 19 additions & 73 deletions grobro/model/growatt_nexa_registers.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 1 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -184,10 +181,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 1 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -263,10 +257,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 2 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -284,10 +275,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 2 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -363,10 +351,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 3 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -384,10 +369,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 3 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -463,10 +445,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 4 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -484,10 +463,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 4 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -563,11 +539,8 @@
"homeassistant": {
"publish": true,
"name": "Slot 5 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"icon": "mdi:clock-start"
"type": "time",
"icon": "mdi:clock-end"
}
},
"slot5_end_time": {
Expand All @@ -584,10 +557,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 5 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -663,10 +633,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 6 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -684,10 +651,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 6 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -763,10 +727,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 7 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -784,10 +745,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 7 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -863,10 +821,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 8 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -884,10 +839,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 8 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down Expand Up @@ -963,10 +915,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 9 Start Time",
"type": "number",
"min": 0,
"max": 2358,
"step": 1,
"type": "time",
"icon": "mdi:clock-start"
}
},
Expand All @@ -984,10 +933,7 @@
"homeassistant": {
"publish": true,
"name": "Slot 9 End Time",
"type": "number",
"min": 0,
"max": 2359,
"step": 1,
"type": "time",
"icon": "mdi:clock-end"
}
},
Expand Down
Loading
Loading