Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 161 additions & 20 deletions custom_components/myhome/climate.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions custom_components/myhome/gateway_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
LOGGER,
)
from .topology import peer_handles_who
from .where_grammar import is_central_thermo_where, is_probe, where_param
from .where_grammar import is_central_unit_report, is_probe, where_param

if TYPE_CHECKING:
from .gateway import MyHOMEGatewayHandler
Expand Down Expand Up @@ -306,7 +306,7 @@ async def process_message(self, message: Any) -> None:
or isinstance(message, OWNHeatingEvent)
):
if not message.is_translation:
if isinstance(message, OWNHeatingEvent) and is_central_thermo_where(message):
if isinstance(message, OWNHeatingEvent) and is_central_unit_report(message):
self.handler.has_central_unit = True
if isinstance(message, OWNLightingEvent) and self._is_active_for_who(1):
if (
Expand Down
9 changes: 9 additions & 0 deletions custom_components/myhome/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,15 @@
"fan_speed_zone_only": {
"message": "{name} has no fan speed of its own: only a zone (1-99) takes one"
},
"auto_standalone_zone": {
"message": "{name} is a standalone thermostat: the central unit has no program for it"
},
"zone_heat_in_conditioning_season": {
"message": "{name} follows the central unit, which is in the conditioning season: change the season on the central unit"
},
"zone_cool_in_heating_season": {
"message": "{name} follows the central unit, which is in the heating season: change the season on the central unit"
},
"command_delivery_cancelled": {
"message": "{name}: direction command delivery was cancelled before reaching the bus"
},
Expand Down
9 changes: 9 additions & 0 deletions custom_components/myhome/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,15 @@
"fan_speed_zone_only": {
"message": "{name} has no fan speed of its own: only a zone (1-99) takes one"
},
"auto_standalone_zone": {
"message": "{name} is a standalone thermostat: the central unit has no program for it"
},
"zone_heat_in_conditioning_season": {
"message": "{name} follows the central unit, which is in the conditioning season: change the season on the central unit"
},
"zone_cool_in_heating_season": {
"message": "{name} follows the central unit, which is in the heating season: change the season on the central unit"
},
"command_delivery_cancelled": {
"message": "{name}: direction command delivery was cancelled before reaching the bus"
},
Expand Down
9 changes: 9 additions & 0 deletions custom_components/myhome/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,15 @@
"fan_speed_zone_only": {
"message": "{name} n'a pas de vitesse de ventilation propre : seule une zone (1-99) peut en avoir une"
},
"auto_standalone_zone": {
"message": "{name} est un thermostat autonome : la centrale n'a pas de programme pour lui"
},
"zone_heat_in_conditioning_season": {
"message": "{name} suit la centrale, qui est en saison de climatisation : changez la saison sur la centrale"
},
"zone_cool_in_heating_season": {
"message": "{name} suit la centrale, qui est en saison de chauffage : changez la saison sur la centrale"
},
"command_delivery_cancelled": {
"message": "{name} : l'envoi de la commande de direction a été annulé avant d'atteindre le bus"
},
Expand Down
9 changes: 9 additions & 0 deletions custom_components/myhome/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,15 @@
"fan_speed_zone_only": {
"message": "{name} non ha una velocità del ventilatore propria: solo una zona (1-99) può averne una"
},
"auto_standalone_zone": {
"message": "{name} è un termostato autonomo: la centrale non ha un programma per esso"
},
"zone_heat_in_conditioning_season": {
"message": "{name} segue la centrale, che è nella stagione di condizionamento: cambia la stagione sulla centrale"
},
"zone_cool_in_heating_season": {
"message": "{name} segue la centrale, che è nella stagione di riscaldamento: cambia la stagione sulla centrale"
},
"command_delivery_cancelled": {
"message": "{name}: l'invio del comando di direzione è stato annullato prima di raggiungere il bus"
},
Expand Down
9 changes: 9 additions & 0 deletions custom_components/myhome/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,15 @@
"fan_speed_zone_only": {
"message": "{name} heeft geen eigen ventilatorsnelheid: alleen een zone (1-99) kan er een krijgen"
},
"auto_standalone_zone": {
"message": "{name} is een zelfstandige thermostaat: de centrale heeft er geen programma voor"
},
"zone_heat_in_conditioning_season": {
"message": "{name} volgt de centrale, die in het koelseizoen staat: wijzig het seizoen op de centrale"
},
"zone_cool_in_heating_season": {
"message": "{name} volgt de centrale, die in het stookseizoen staat: wijzig het seizoen op de centrale"
},
"command_delivery_cancelled": {
"message": "{name}: de aflevering van het richtingscommando is geannuleerd voordat het de bus bereikte"
},
Expand Down
29 changes: 29 additions & 0 deletions custom_components/myhome/where_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def _sound_step_delta(self: Any) -> int | None:
"is_actuator_where",
"is_apl_address",
"is_central_thermo_where",
"is_central_unit_report",
"is_probe",
"is_pump",
"is_valid_hs_tuple",
Expand Down Expand Up @@ -290,6 +291,34 @@ def is_central_thermo_where(message: Any) -> bool:
return raw.startswith("#") and head.isdigit() and not is_probe(head)


# Zone operation mode by central unit with its season (WHO 4 p. 65, 1.9.4; p. 9): 110 / 210
# manual, 111 / 211 automatic, 103 / 203 off, 102 / 202 protection. The generic 310 / 311
# (p. 9 lists them on the monitor session too) are also the command text, and an F455
# makes up *4*311*#Z## for a standalone thermostat; the live 3550 reported its zone 4 as
# *4*210*#4##, also in its quarter-hour push, so a central unit is still found.
_CENTRAL_ZONE_STATUS_WHATS = frozenset({"110", "210", "111", "211", "103", "203", "102", "202"})


def is_central_unit_report(message: Any) -> bool:
"""Whether a WHO=4 frame shows that the plant has a central unit.

``#0`` and ``#0#N`` are the central unit itself (WHO 4 p. 6, p. 64). A ``#Z`` frame counts
when it is a dimension or a zone status with its season (WHO 4 p. 65). A generic one does
not: an F455 puts ``*4*311*#2##`` on its event session after every status of a standalone
thermostat, on a plant with or without a central unit (live, 2026-11-08).
"""
if not is_central_thermo_where(message):
return False
raw = str(getattr(message, "where", None) or "").strip().removeprefix("4-")
if raw.removeprefix("#").split("#", 1)[0] == "0":
return True
frame = str(message)
if not frame.startswith("*4*"):
return True
what = frame.split("*")[2].split("#", 1)[0]
return what in _CENTRAL_ZONE_STATUS_WHATS


def canonical_point_spelling(where: str) -> str:
"""``0004`` -> ``04``, silently; anything else as typed.

Expand Down
Loading
Loading