Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update as/topic branch with changes on dev #372

Merged
merged 20 commits into from
Oct 29, 2024
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
1 change: 1 addition & 0 deletions CodeGenerationTools/GridworksCore/DataSchema.odxml

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions CodeGenerationTools/GridworksCore/Types/DeriveTypes.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,6 @@ from pydantic import BaseModel</xsl:text>
and not(PropertyFormat = 'UTCSeconds')
and not(PropertyFormat = 'PositiveInteger')
])>0">
<xsl:text>, StrictInt # Count:</xsl:text>
<xsl:value-of select="count($airtable//TypeAttributes/TypeAttribute[(VersionedType = $versioned-type-id)
and (PrimitiveType = 'Integer')
and not(PropertyFormat = 'UTCMilliseconds')
and not(PropertyFormat = 'UTCSeconds')
and not(PropertyFormat = 'PositiveInteger')
])>0"/>
</xsl:if>


Expand Down
6 changes: 6 additions & 0 deletions src/gwproto/data_classes/hardware_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ def channel(self, name: str, default: Any = None) -> DataChannel: # noqa: ANN40
def node(self, name: str, default: Any = None) -> ShNode: # noqa: ANN401
return self.nodes.get(name, default)

def node_by_handle(self, handle: str) -> Optional[ShNode]:
d = {node.Handle: node for node in self.nodes.values() if node.Handle}
if handle in d:
return d[handle]
return None

def component(self, node_name: str) -> Optional[Component]:
return self.component_from_node(self.node(node_name, None))

Expand Down
6 changes: 3 additions & 3 deletions src/gwproto/data_classes/house_0_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ class H0CN:
dist_flow = H0N.dist_flow
primary_flow = H0N.primary_flow
store_flow = H0N.store_flow
dist_flow_integrated = f"{H0N.dist_flow}-integrated"
primary_flow_integrated = f"{H0N.primary_flow}-integrated"
store_flow_integrated = f"{H0N.store_flow}-integrated"
dist_flow_hz = f"{H0N.dist_flow}-hz"
primary_flow_hz = f"{H0N.primary_flow}-hz"
store_flow_hz = f"{H0N.store_flow}-hz"

def __init__(self, total_store_tanks: int, zone_list: List[str]) -> None:
for i in range(total_store_tanks):
Expand Down
4 changes: 4 additions & 0 deletions src/gwproto/enums/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
from gwproto.enums.fsm_event_type import FsmEventType
from gwproto.enums.fsm_name import FsmName
from gwproto.enums.fsm_report_type import FsmReportType
from gwproto.enums.gpm_from_hz_method import GpmFromHzMethod
from gwproto.enums.hz_calc_method import HzCalcMethod
from gwproto.enums.kind_of_param import KindOfParam
from gwproto.enums.make_model import MakeModel
from gwproto.enums.relay_closed_or_open import RelayClosedOrOpen
Expand Down Expand Up @@ -71,6 +73,8 @@
"FsmEventType", # [sh.fsm.event.type.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shfsmeventtype)
"FsmName", # [sh.fsm.name.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#shfsmname)
"FsmReportType", # [fsm.report.type.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#fsmreporttype)
"GpmFromHzMethod", # [gpm.from.hz.method.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#gpmfromhzmethod)
"HzCalcMethod", # [hz.calc.method.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#hzcalcmethod)
"KindOfParam", # [spaceheat.kind.of.param.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatkindofparam)
"MakeModel", # [spaceheat.make.model.003](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#spaceheatmakemodel)
"RelayClosedOrOpen", # [relay.closed.or.open.000](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#relayclosedoropen)
Expand Down
4 changes: 2 additions & 2 deletions src/gwproto/enums/actor_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ActorClass(GwStrEnum):
- HoneywellThermostat: An actor for representing a Honeywell Hubitat thermostat
which can load thermostat heating state change messages into status reports.
- ApiTankModule
- ApiFlowMeter
- ApiFlowModule

For more information:
- [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/)
Expand Down Expand Up @@ -95,7 +95,7 @@ class ActorClass(GwStrEnum):
Hubitat = auto()
HoneywellThermostat = auto()
ApiTankModule = auto()
ApiFlowMeter = auto()
ApiFlowModule = auto()

@classmethod
def default(cls) -> "ActorClass":
Expand Down
34 changes: 34 additions & 0 deletions src/gwproto/enums/gpm_from_hz_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from enum import auto
from typing import List

from gw.enums import GwStrEnum


class GpmFromHzMethod(GwStrEnum):
"""

Values:
- Constant

For more information:
- [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/)
- [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#gpmfromhzmethod)
"""

Constant = auto()

@classmethod
def default(cls) -> "GpmFromHzMethod":
return cls.Constant

@classmethod
def values(cls) -> List[str]:
return [elt.value for elt in cls]

@classmethod
def enum_name(cls) -> str:
return "gpm.from.hz.method"

@classmethod
def enum_version(cls) -> str:
return "000"
36 changes: 36 additions & 0 deletions src/gwproto/enums/hz_calc_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from enum import auto
from typing import List

from gw.enums import GwStrEnum


class HzCalcMethod(GwStrEnum):
"""

Values:
- BasicExpWeightedAvg
- BasicButterWorth

For more information:
- [ASLs](https://gridworks-type-registry.readthedocs.io/en/latest/)
- [Global Authority](https://gridworks-type-registry.readthedocs.io/en/latest/enums.html#hzcalcmethod)
"""

BasicExpWeightedAvg = auto()
BasicButterWorth = auto()

@classmethod
def default(cls) -> "HzCalcMethod":
return cls.BasicExpWeightedAvg

@classmethod
def values(cls) -> List[str]:
return [elt.value for elt in cls]

@classmethod
def enum_name(cls) -> str:
return "hz.calc.method"

@classmethod
def enum_version(cls) -> str:
return "000"
4 changes: 2 additions & 2 deletions src/gwproto/enums/telemetry_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class TelemetryName(GwStrEnum):
report cumulative gallons as their raw output. Example: 55300 means 55.3 gallons.
- VoltageRmsMilliVolts: Voltage in Root Mean Square MilliVolts.
- MilliWattHours: Energy in MilliWattHours.
- FrequencyMicroHz: Frequency in MicroHz. Example: 59,965,332 means 59.965332 Hz.
- MicroHz: Frequency in MicroHz. Example: 59,965,332 means 59.965332 Hz.
- AirTempCTimes1000: Air temperature, in Degrees Celsius multiplied by 1000. Example:
6234 means 6.234 deg Celcius.
- AirTempFTimes1000: Air temperature, in Degrees F multiplied by 1000. Example:
Expand All @@ -48,7 +48,7 @@ class TelemetryName(GwStrEnum):
GallonsTimes100 = auto()
VoltageRmsMilliVolts = auto()
MilliWattHours = auto()
FrequencyMicroHz = auto()
MicroHz = auto()
AirTempCTimes1000 = auto()
AirTempFTimes1000 = auto()
ThermostatState = auto()
Expand Down
8 changes: 8 additions & 0 deletions src/gwproto/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
from gwproto.types.spaceheat_node_gt import SpaceheatNodeGt
from gwproto.types.synced_readings import SyncedReadings
from gwproto.types.tank_module_params import TankModuleParams
from gwproto.types.ticklist_hall import TicklistHall
from gwproto.types.ticklist_hall_report import TicklistHallReport
from gwproto.types.ticklist_reed import TicklistReed
from gwproto.types.ticklist_reed_report import TicklistReedReport
from gwproto.types.web_server_component_gt import WebServerComponentGt

__all__ = [
Expand Down Expand Up @@ -83,6 +87,10 @@
"SpaceheatNodeGt",
"SyncedReadings",
"TankModuleParams",
"TicklistHall",
"TicklistHallReport",
"TicklistReed",
"TicklistReedReport",
"WebServerComponentGt",
"cacs", # noqa: F822
"components", # noqa: F822
Expand Down
28 changes: 26 additions & 2 deletions src/gwproto/types/pico_flow_module_component_gt.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
import re
from typing import Literal, Optional

from gwproto.enums import MakeModel
from pydantic import field_validator

from gwproto.enums import GpmFromHzMethod, HzCalcMethod, MakeModel
from gwproto.property_format import SpaceheatName
from gwproto.types.component_gt import ComponentGt


class PicoFlowModuleComponentGt(ComponentGt):
Enabled: bool
PicoHwUid: str
SerialNumber: str
FlowNodeName: SpaceheatName
FlowMeterType: MakeModel = MakeModel.SAIER__SENHZG1WA
HzCalcMethod: HzCalcMethod
GpmFromHzMethod: GpmFromHzMethod
ConstantGallonsPerTick: float
SendHz: bool = True
SendGallons: bool = False
SendTickLists: bool = False
NoFlowMs: int
AsyncCaptureThresholdGpmTimes100: int
PublishEmptyTicklistAfterS: Optional[int] = None # Hall Params
PublishAnyTicklistAfterS: Optional[int] = None # Reed Params
PublishTicklistPeriodS: Optional[int] = None # Required for Hall Params
PublishTicklistLength: Optional[int] = None # required for Reed Params
ExpAlpha: Optional[float] = 0.5
CutoffFrequency: Optional[float] = 1.25
TypeName: Literal["pico.flow.module.component.gt"] = "pico.flow.module.component.gt"
Version: Literal["000"] = "000"

@field_validator("HwUid")
@classmethod
def check_config_list(cls, v: str) -> str:
"""
Axiom 1: HwUid is of the form 'pico_xxxxxx' where xxxxxx
are lowercase hex (the last digits of its pico W hw id)
"""
pattern = r"^pico_[0-9a-f]{6}$"
if not bool(re.match(pattern, v)):
raise ValueError("HwUid should be the pico hwuid, eg pico_60e352")
return v
4 changes: 2 additions & 2 deletions src/gwproto/types/pico_tank_module_component_gt.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

class PicoTankModuleComponentGt(ComponentGt):
Enabled: bool
PicoAHwUid: Optional[str] = None
PicoBHwUid: Optional[str] = None
TempCalcMethod: TempCalcMethod
ThermistorBeta: int
SendMicroVolts: bool
Samples: int
NumSampleAverages: int
PicoAHwUid: Optional[str] = None
PicoBHwUid: Optional[str] = None
PicoKOhms: Optional[int] = None
TypeName: Literal["pico.tank.module.component.gt"] = "pico.tank.module.component.gt"
Version: Literal["000"] = "000"
Expand Down
11 changes: 8 additions & 3 deletions src/gwproto/types/tank_module_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@

from pydantic import BaseModel, PositiveInt, field_validator

from gwproto.property_format import SpaceheatName
from gwproto.property_format import (
SpaceheatName,
)


class TankModuleParams(BaseModel):
"""
Parameters expected by a GridWorks TankModule2
"""

HwUid: str
ActorNodeName: SpaceheatName
PicoAB: str
Expand All @@ -25,6 +31,5 @@ def check_pico_a_b(cls, v: str) -> str:
"""
Axiom 1: "PicoAB must be a or b"
"""
if v not in {"a", "b"}:
raise ValueError(f"PicoAB must be lowercase a or lowercase b, not <{v}>")
# Implement Axiom(s)
return v
34 changes: 34 additions & 0 deletions src/gwproto/types/ticklist_hall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Type ticklist.hall, version 101"""

from typing import List, Literal, Optional

from pydantic import BaseModel, StrictInt, model_validator # Count:true
from typing_extensions import Self


class TicklistHall(BaseModel):
HwUid: str
FirstTickTimestampNanoSecond: Optional[StrictInt] = None
RelativeMicrosecondList: List[StrictInt]
PicoBeforePostTimestampNanoSecond: StrictInt
TypeName: Literal["ticklist.hall"] = "ticklist.hall"
Version: Literal["101"] = "101"

@model_validator(mode="after")
def check_axiom_1(self) -> Self:
"""
Axiom 1: FirstTickTimestampNanoSecond is none iff RelativeMicrosecondList has length 0.

"""
if (
self.FirstTickTimestampNanoSecond is None
and len(self.RelativeMicrosecondList) > 0
):
raise ValueError(
"FirstTickTimestampNanoSecond is None but RelativeMicrosecondList has nonzero length!"
)
if self.FirstTickTimestampNanoSecond and len(self.RelativeMicrosecondList) == 0:
raise ValueError(
"FirstTickTimestampNanoSecond exists but RelativeMicrosecondList has no elements!"
)
return self
21 changes: 21 additions & 0 deletions src/gwproto/types/ticklist_hall_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Type ticklist.hall.report, version 000"""

from typing import Literal

from pydantic import BaseModel

from gwproto.property_format import LeftRightDotStr, SpaceheatName, UTCMilliseconds
from gwproto.types.ticklist_hall import TicklistHall


class TicklistHallReport(BaseModel):
"""
Used by the SCADA to forward a ticklist.hall message received from a PicoFlowHall module.
"""

TerminalAssetAlias: LeftRightDotStr
ChannelName: SpaceheatName
ScadaReceivedUnixMs: UTCMilliseconds
Ticklist: TicklistHall
TypeName: Literal["ticklist.hall.report"] = "ticklist.hall.report"
Version: Literal["000"] = "000"
34 changes: 34 additions & 0 deletions src/gwproto/types/ticklist_reed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Type ticklist.reed, version 101"""

from typing import List, Literal, Optional

from pydantic import BaseModel, StrictInt, model_validator
from typing_extensions import Self


class TicklistReed(BaseModel):
HwUid: str
FirstTickTimestampNanoSecond: Optional[StrictInt] = None
RelativeMillisecondList: List[StrictInt]
PicoBeforePostTimestampNanoSecond: StrictInt
TypeName: Literal["ticklist.reed"] = "ticklist.reed"
Version: Literal["101"] = "101"

@model_validator(mode="after")
def check_axiom_1(self) -> Self:
"""
Axiom 1: .
FirstTickTimestampNanoSecond is None iff RelativeMillisecondList has length 0
"""
if (
self.FirstTickTimestampNanoSecond is None
and len(self.RelativeMillisecondList) > 0
):
raise ValueError(
"FirstTickTimestampNanoSecond is None but RelativeMillisecondList has nonzero length!"
)
if self.FirstTickTimestampNanoSecond and len(self.RelativeMillisecondList) == 0:
raise ValueError(
"FirstTickTimestampNanoSecond exists but RelativeMillisecondList has no elements!"
)
return self
21 changes: 21 additions & 0 deletions src/gwproto/types/ticklist_reed_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Type ticklist.reed.report, version 000"""

from typing import Literal

from pydantic import BaseModel # Count:true

from gwproto.property_format import LeftRightDotStr, SpaceheatName, UTCMilliseconds
from gwproto.types.ticklist_reed import TicklistReed


class TicklistReedReport(BaseModel):
"""
Used by the SCADA to forward a ticklist.reed message received from a PicoFlowReed module.
"""

TerminalAssetAlias: LeftRightDotStr
ChannelName: SpaceheatName
ScadaReceivedUnixMs: UTCMilliseconds
Ticklist: TicklistReed
TypeName: Literal["ticklist.reed.report"] = "ticklist.reed.report"
Version: Literal["000"] = "000"
2 changes: 1 addition & 1 deletion tests/config/hardware-layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"MilliWattHours",
"VoltageRmsMilliVolts",
"CurrentRmsMicroAmps",
"FrequencyMicroHz"
"MicroHz"
],
"TypeName": "electric.meter.cac.gt",
"Version": "001"
Expand Down
Loading
Loading