-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tank Module via HTTP/Hubitat/Fibaro (#205)
This PR adds support for Tank modules accessed by sending HTTP polls to a Hubitat which is configured to communicate with Fibaro Smart Implant temperature sensors on the tank via Fibaro / Hubitat ZWave network. Changes: 1. New CACS, Components and enums added for Fibaro Smart Implant, Hubitat, Hubitat Tank Module and generic RESTPoller. 2. HardwareLayout has been modified: 1. Components and CACS in OtherComponents/OtherCACs can be decoded from TypeName without need to specify the decoding class. 2. A "resolve" phase has been added after loading CACs, Components and Nodes to allow validation of relationships between entities when that validation requires all CACs, Components and Nodes to have been decoded. 3. Code generation has been broken by this merge. Fixing up code generation will be addressed by #214. More code generation discussion in #215.
- Loading branch information
1 parent
619e793
commit 353386c
Showing
33 changed files
with
2,094 additions
and
64 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "gridworks-protocol" | ||
version = "0.5.7" | ||
version = "0.6.0" | ||
description = "Gridworks Protocol" | ||
authors = ["Jessica Millar <[email protected]>"] | ||
license = "MIT" | ||
|
@@ -24,6 +24,7 @@ pydantic = "^1.10.2" | |
pendulum = "^2.1.2" | ||
fastapi-utils = "^0.2.1" | ||
gridworks = "^0.2.9" | ||
yarl = "^1.9.2" | ||
|
||
[tool.poetry.dev-dependencies] | ||
Pygments = ">=2.10.0" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from gwproto.data_classes.component_attribute_class import ComponentAttributeClass | ||
|
||
|
||
class FibaroSmartImplantCac(ComponentAttributeClass): | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from gwproto.data_classes.component_attribute_class import ComponentAttributeClass | ||
|
||
|
||
class HubitatCac(ComponentAttributeClass): | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from gwproto.data_classes.component_attribute_class import ComponentAttributeClass | ||
|
||
|
||
class HubitatTankModuleCac(ComponentAttributeClass): | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from gwproto.data_classes.component_attribute_class import ComponentAttributeClass | ||
|
||
|
||
class RESTPollerCac(ComponentAttributeClass): | ||
... |
19 changes: 19 additions & 0 deletions
19
src/gwproto/data_classes/components/fibaro_smart_implant_component.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from typing import Optional | ||
|
||
from gwproto.data_classes.component import Component | ||
|
||
|
||
class FibaroSmartImplantComponent(Component): | ||
def __init__( | ||
self, | ||
component_id: str, | ||
component_attribute_class_id: str, | ||
display_name: Optional[str] = None, | ||
hw_uid: Optional[str] = None, | ||
): | ||
super().__init__( | ||
component_id=component_id, | ||
component_attribute_class_id=component_attribute_class_id, | ||
display_name=display_name, | ||
hw_uid=hw_uid, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from typing import Optional | ||
|
||
import yarl | ||
|
||
from gwproto.data_classes.component import Component | ||
from gwproto.types.hubitat_gt import HubitatGt | ||
|
||
|
||
class HubitatComponent(Component): | ||
hubitat_gt: HubitatGt | ||
|
||
def __init__( | ||
self, | ||
component_id: str, | ||
component_attribute_class_id: str, | ||
hubitat_gt: HubitatGt, | ||
display_name: Optional[str] = None, | ||
hw_uid: Optional[str] = None, | ||
): | ||
self.hubitat_gt = hubitat_gt | ||
super().__init__( | ||
component_id=component_id, | ||
component_attribute_class_id=component_attribute_class_id, | ||
display_name=display_name, | ||
hw_uid=hw_uid, | ||
) | ||
|
||
def urls(self) -> dict[str, Optional[yarl.URL]]: | ||
return self.hubitat_gt.urls() |
113 changes: 113 additions & 0 deletions
113
src/gwproto/data_classes/components/hubitat_tank_component.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
from typing import Optional | ||
|
||
import yarl | ||
|
||
from gwproto.data_classes.component import Component | ||
from gwproto.data_classes.components.hubitat_component import HubitatComponent | ||
from gwproto.data_classes.resolver import ComponentResolver | ||
from gwproto.data_classes.sh_node import ShNode | ||
from gwproto.types.hubitat_component_gt import HubitatComponentGt | ||
from gwproto.types.hubitat_component_gt import HubitatRESTResolutionSettings | ||
from gwproto.types.hubitat_gt import HubitatGt | ||
from gwproto.types.hubitat_tank_gt import FibaroTempSensorSettings | ||
from gwproto.types.hubitat_tank_gt import FibaroTempSensorSettingsGt | ||
from gwproto.types.hubitat_tank_gt import HubitatTankSettingsGt | ||
from gwproto.types.telemetry_reporting_config import TelemetryReportingConfig | ||
|
||
|
||
class HubitatTankComponent(Component, ComponentResolver): | ||
hubitat: HubitatComponentGt | ||
sensor_supply_voltage: float | ||
devices_gt: list[FibaroTempSensorSettingsGt] | ||
devices: list[FibaroTempSensorSettings] = [] | ||
|
||
def __init__( | ||
self, | ||
component_id: str, | ||
component_attribute_class_id: str, | ||
tank_gt: HubitatTankSettingsGt, | ||
display_name: Optional[str] = None, | ||
hw_uid: Optional[str] = None, | ||
): | ||
# Create self.hubitat as a proxy containing only the id | ||
# of the hubitat; the actual component data will be resolved | ||
# when resolve() is called; Here in the constructor we cannot | ||
# rely on the actual HubitatComponentGt existing yet. | ||
self.hubitat = HubitatComponentGt( | ||
ComponentId=tank_gt.hubitat_component_id, | ||
ComponentAttributeClassId="00000000-0000-0000-0000-000000000000", | ||
Hubitat=HubitatGt( | ||
Host="", | ||
MakerApiId=-1, | ||
AccessToken="", | ||
MacAddress="000000000000", | ||
), | ||
) | ||
self.sensor_supply_voltage = tank_gt.sensor_supply_voltage | ||
self.devices_gt = list(tank_gt.devices) | ||
super().__init__( | ||
display_name=display_name, | ||
component_id=component_id, | ||
hw_uid=hw_uid, | ||
component_attribute_class_id=component_attribute_class_id, | ||
) | ||
|
||
def resolve( | ||
self, | ||
tank_node_name: str, | ||
nodes: dict[str, ShNode], | ||
components: dict[str, Component], | ||
): | ||
hubitat_component = components.get(self.hubitat.ComponentId, None) | ||
if hubitat_component is None: | ||
raise ValueError( | ||
"ERROR. No component found for " | ||
f"HubitatTankComponent.hubitat.CompnentId {self.hubitat.ComponentId}" | ||
) | ||
if not isinstance(hubitat_component, HubitatComponent): | ||
raise ValueError( | ||
"ERROR. Referenced hubitat component has type " | ||
f"{type(hubitat_component)}; " | ||
"must be instance of HubitatComponent. " | ||
f"Hubitat component id: {self.hubitat.ComponentId}" | ||
) | ||
hubitat_component_gt = HubitatComponentGt.from_data_class(hubitat_component) | ||
hubitat_settings = HubitatRESTResolutionSettings(hubitat_component_gt) | ||
devices = [ | ||
FibaroTempSensorSettings.create( | ||
tank_name=tank_node_name, | ||
settings_gt=device_gt, | ||
hubitat=hubitat_settings, | ||
) | ||
for device_gt in self.devices_gt | ||
if device_gt.enabled | ||
] | ||
for device in devices: | ||
if device.node_name not in nodes: | ||
raise ValueError( | ||
f"ERROR. Node not found for tank temp sensor <{device.node_name}>" | ||
) | ||
# replace proxy hubitat component, which only had component id. | ||
# with the actual hubitat component containing data. | ||
self.hubitat = hubitat_component_gt | ||
self.devices = devices | ||
|
||
def urls(self) -> dict[str, Optional[yarl.URL]]: | ||
urls = self.hubitat.urls() | ||
for device in self.devices: | ||
urls[device.node_name] = device.url | ||
return urls | ||
|
||
@property | ||
def config_list(self) -> list[TelemetryReportingConfig]: | ||
return [ | ||
TelemetryReportingConfig( | ||
TelemetryName=device.telemetry_name, | ||
AboutNodeName=device.node_name, | ||
ReportOnChange=False, | ||
SamplePeriodS=int(device.rest.poll_period_seconds), | ||
Exponent=device.exponent, | ||
Unit=device.unit, | ||
) | ||
for device in self.devices | ||
] |
24 changes: 24 additions & 0 deletions
24
src/gwproto/data_classes/components/rest_poller_component.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from typing import Optional | ||
|
||
from gwproto.data_classes.component import Component | ||
from gwproto.types.rest_poller_gt import RESTPollerSettings | ||
|
||
|
||
class RESTPollerComponent(Component): | ||
rest: RESTPollerSettings | ||
|
||
def __init__( | ||
self, | ||
component_id: str, | ||
component_attribute_class_id: str, | ||
rest: RESTPollerSettings, | ||
display_name: Optional[str] = None, | ||
hw_uid: Optional[str] = None, | ||
): | ||
self.rest = rest | ||
super().__init__( | ||
display_name=display_name, | ||
component_id=component_id, | ||
hw_uid=hw_uid, | ||
component_attribute_class_id=component_attribute_class_id, | ||
) |
Oops, something went wrong.