Skip to content

Commit

Permalink
Refactored CM10 sensor
Browse files Browse the repository at this point in the history
  • Loading branch information
faanskit committed Jan 23, 2024
1 parent ac00d7f commit 12335f9
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 97 deletions.
89 changes: 48 additions & 41 deletions custom_components/checkwatt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class CheckwattResp(TypedDict):
update_time: str
next_update_time: str
fcr_d_status: str
fcr_d_state: str
fcr_d_info: str
fcr_d_date: str
total_solar_energy: float
total_charging_energy: float
Expand Down Expand Up @@ -136,40 +136,35 @@ async def getPeakData(cw_inst):
return (charge_peak_ac, charge_peak_dc, discharge_peak_ac, discharge_peak_dc)


def extract_cm10_status(cw_inst):
def extract_fcrd_status(cw_inst):
"""Extract status from data and logbook."""
fcrd_state = None
meter_status = None

if cw_inst.customer_details is None:
return None

if cw_inst.meter_data is None:
return None

meter_status = cw_inst.meter_status
if meter_status == "offline":
return "Offline"

if meter_status != "producing":
return None
return (None, None, None, None)

pattern = re.compile(r"\[ FCR-D (ACTIVATED|DEACTIVATE|FAIL ACTIVATION) \]")
pattern = re.compile(
r"\[ FCR-D (ACTIVATED|DEACTIVATE|FAIL ACTIVATION) \](?:.*?(\d+,\d+/\d+,\d+/\d+,\d+ %))?(?:\s*(.*?))?(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})"
)
for entry in cw_inst.logbook_entries:
match = pattern.search(entry)
if match:
fcrd_state = match.group(1)
if fcrd_state == "ACTIVATED":
return "Active"
if fcrd_state == "DEACTIVATE":
return "Failed FCR-D"
if fcrd_state == "FAIL ACTIVATION":
return "Failed test"

if cw_inst.meter_under_test:
return "Test pending"

return None
fcrd_percentage = (
match.group(2)
if fcrd_state in ["ACTIVATED", "FAIL ACTIVATION"]
else None
)
error_info = match.group(3) if fcrd_state == "DEACTIVATE" else None
fcrd_timestamp = match.group(4)
if fcrd_percentage is not None:
fcrd_info = fcrd_percentage
elif error_info is not None:
fcrd_info = error_info
else:
fcrd_info = None
break

return (fcrd_state, fcrd_info, fcrd_timestamp)


class CheckwattCoordinator(DataUpdateCoordinator[CheckwattResp]):
Expand Down Expand Up @@ -199,7 +194,7 @@ def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
self.energy_provider = None
self.random_offset = random.randint(0, 14)
self.fcrd_state = None
self.fcrd_percentage = None
self.fcrd_info = None
self.fcrd_timestamp = None
self._id = None
self.update_no = 0
Expand Down Expand Up @@ -237,6 +232,12 @@ async def _async_update_data(self) -> CheckwattResp: # noqa: C901
_LOGGER.error("Failed to get energy flows, abort update")
raise UpdateFailed("Unknown error get_energy_flow")

(
fcrd_state,
fcrd_info,
fcrd_timestamp,
) = extract_fcrd_status(cw_inst)

# Prevent slow funcion to be called at boot.
# The revenue sensors will be updated after ca 1 min
self.update_no += 1
Expand All @@ -253,9 +254,7 @@ async def _async_update_data(self) -> CheckwattResp: # noqa: C901
)

# Store fcrd_state at boot, used to spark event
self.fcrd_state = cw_inst.fcrd_state
self.fcrd_percentage = cw_inst.fcrd_percentage
self.fcrd_timestamp = cw_inst.fcrd_timestamp
self.fcrd_state = fcrd_state
self._id = cw_inst.customer_details["Id"]

else:
Expand Down Expand Up @@ -320,9 +319,9 @@ async def _async_update_data(self) -> CheckwattResp: # noqa: C901
"display_name": cw_inst.customer_details["Meter"][0]["DisplayName"],
"update_time": self.update_time,
"next_update_time": self.next_update_time,
"fcr_d_status": cw_inst.fcrd_state,
"fcr_d_state": cw_inst.fcrd_percentage,
"fcr_d_date": cw_inst.fcrd_timestamp,
"fcr_d_status": fcrd_state,
"fcr_d_info": fcrd_info,
"fcr_d_date": fcrd_timestamp,
"battery_charge_peak": cw_inst.battery_charge_peak,
"battery_discharge_peak": cw_inst.battery_discharge_peak,
"dso": cw_inst.battery_registration["Dso"],
Expand Down Expand Up @@ -366,20 +365,28 @@ async def _async_update_data(self) -> CheckwattResp: # noqa: C901
resp["price_zone"] = cw_inst.price_zone

if cw_inst.meter_data is not None and use_cm10_sensor:
resp["cm10_status"] = extract_cm10_status(cw_inst)
if cw_inst.meter_status == "offline":
resp["cm10_status"] = "Offline"
elif cw_inst.meter_under_test:
resp["cm10_status"] = "Test Pending"
else:
resp["cm10_status"] = "Active"

resp["cm10_version"] = cw_inst.meter_version
resp["cm10_under_test"] = cw_inst.meter_under_test
resp["cm10_status_date"] = cw_inst.meter_status_date

# Check if FCR-D State has changed and dispatch it ACTIVATED/ DEACTIVATED
old_state = self.fcrd_state
new_state = cw_inst.fcrd_state
new_state = fcrd_state

# During test, toggle (every minute)
if BASIC_TEST is True:
if old_state == "ACTIVATED":
new_state = "DEACTIVATE"
if old_state == "DEACTIVATE":
new_state = "FAIL ACTIVATION"
if old_state == "FAIL ACTIVATION":
new_state = "ACTIVATED"

if old_state != new_state:
Expand All @@ -388,13 +395,13 @@ async def _async_update_data(self) -> CheckwattResp: # noqa: C901
"data": {
"current_fcrd": {
"state": old_state,
"status": self.fcrd_percentage,
"info": self.fcrd_info,
"date": self.fcrd_timestamp,
},
"new_fcrd": {
"state": new_state,
"status": cw_inst.fcrd_percentage,
"date": cw_inst.fcrd_timestamp,
"info": fcrd_info,
"date": fcrd_timestamp,
},
},
}
Expand All @@ -408,8 +415,8 @@ async def _async_update_data(self) -> CheckwattResp: # noqa: C901

# Update self to discover next change
self.fcrd_state = new_state
self.fcrd_percentage = cw_inst.fcrd_percentage
self.fcrd_timestamp = cw_inst.fcrd_timestamp
self.fcrd_info = fcrd_info
self.fcrd_timestamp = fcrd_timestamp

return resp

Expand Down
4 changes: 2 additions & 2 deletions custom_components/checkwatt/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
P_UNKNOWN = "Unknown"

# Temp Test
BASIC_TEST = False
BASIC_TEST = True

# CheckWatt Sensor Attributes
# NOTE Keep these names aligned with strings.json
Expand All @@ -42,7 +42,7 @@
C_GRID_POWER = "grid_power"
C_ENERGY_PROVIDER = "energy_provider"
C_FCRD_DATE = "fcr_d_date"
C_FCRD_STATE = "fcr_d_state"
C_FCRD_INFO = "fcr_d_info"
C_FCRD_STATUS = "fcr_d_status"
C_NEXT_UPDATE_TIME = "next_update"
C_PRICE_ZONE = "price_zone"
Expand Down
12 changes: 11 additions & 1 deletion custom_components/checkwatt/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

EVENT_FCRD_ACTIVATED = "fcrd_activated"
EVENT_FCRD_DEACTIVATED = "fcrd_deactivated"
EVENT_FCRD_FAILED = "fcrd_failed"

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -83,7 +84,11 @@ def device_info(self) -> DeviceInfo:
class CheckWattFCRDEvent(AbstractCheckwattEvent):
"""Representation of a CheckWatt sleep event."""

_attr_event_types = [EVENT_FCRD_ACTIVATED, EVENT_FCRD_DEACTIVATED]
_attr_event_types = [
EVENT_FCRD_ACTIVATED,
EVENT_FCRD_DEACTIVATED,
EVENT_FCRD_FAILED,
]

def __init__(
self,
Expand Down Expand Up @@ -112,6 +117,8 @@ async def async_added_to_hass(self) -> None:
event = EVENT_FCRD_ACTIVATED
elif self._coordinator.data["fcr_d_status"] == "DEACTIVATE":
event = EVENT_FCRD_DEACTIVATED
elif self._coordinator.data["fcr_d_status"] == "FAIL ACTIVATION":
event = EVENT_FCRD_FAILED

if event is not None:
self._trigger_event(event)
Expand All @@ -130,6 +137,9 @@ def handle_event(self, signal_payload) -> None:
event = EVENT_FCRD_ACTIVATED
elif signal_payload["data"]["new_fcrd"]["state"] == "DEACTIVATE":
event = EVENT_FCRD_DEACTIVATED
elif signal_payload["data"]["new_fcrd"]["state"] == "FAIL ACTIVATION":
event = EVENT_FCRD_FAILED

else:
_LOGGER.error(
"Signal %s payload did not include correct data", EVENT_SIGNAL_FCRD
Expand Down
50 changes: 25 additions & 25 deletions custom_components/checkwatt/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
C_DSO,
C_ENERGY_PROVIDER,
C_FCRD_DATE,
C_FCRD_STATE,
C_FCRD_INFO,
C_FCRD_STATUS,
C_GRID_POWER,
C_NEXT_UPDATE_TIME,
Expand Down Expand Up @@ -333,18 +333,6 @@ def __init__(
self._attr_extra_state_attributes.update(
{C_NEXT_UPDATE_TIME: self._coordinator.data["next_update_time"]}
)
if "fcr_d_status" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_STATUS: self._coordinator.data["fcr_d_status"]}
)
if "fcr_d_state" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_STATE: self._coordinator.data["fcr_d_state"]}
)
if "fcr_d_date" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_DATE: self._coordinator.data["fcr_d_date"]}
)
if "battery_charge_peak" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_CHARGE_PEAK: self._coordinator.data["battery_charge_peak"]}
Expand Down Expand Up @@ -412,18 +400,6 @@ def _handle_coordinator_update(self) -> None:
self._attr_extra_state_attributes.update(
{C_NEXT_UPDATE_TIME: self._coordinator.data["next_update_time"]}
)
if "fcr_d_status" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_STATUS: self._coordinator.data["fcr_d_status"]}
)
if "fcr_d_state" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_STATE: self._coordinator.data["fcr_d_state"]}
)
if "fcr_d_date" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_DATE: self._coordinator.data["fcr_d_date"]}
)
if "battery_charge_peak" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_CHARGE_PEAK: self._coordinator.data["battery_charge_peak"]}
Expand Down Expand Up @@ -759,6 +735,18 @@ async def async_update(self) -> None:
self._attr_extra_state_attributes.update(
{C_CM10_STATUS_DATE: self._coordinator.data["cm10_status_date"]}
)
if "fcr_d_status" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_STATUS: self._coordinator.data["fcr_d_status"]}
)
if "fcr_d_info" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_INFO: self._coordinator.data["fcr_d_info"]}
)
if "fcr_d_date" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_DATE: self._coordinator.data["fcr_d_date"]}
)
self._attr_available = True

@callback
Expand All @@ -782,6 +770,18 @@ def _handle_coordinator_update(self) -> None:
self._attr_extra_state_attributes.update(
{C_CM10_STATUS_DATE: self._coordinator.data["cm10_status_date"]}
)
if "fcr_d_status" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_STATUS: self._coordinator.data["fcr_d_status"]}
)
if "fcr_d_info" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_INFO: self._coordinator.data["fcr_d_info"]}
)
if "fcr_d_date" in self._coordinator.data:
self._attr_extra_state_attributes.update(
{C_FCRD_DATE: self._coordinator.data["fcr_d_date"]}
)
super()._handle_coordinator_update()

@property
Expand Down
18 changes: 9 additions & 9 deletions custom_components/checkwatt/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,6 @@
"next_update": {
"name": "Next update"
},
"fcr_d_state": {
"name": "FCR-D State"
},
"fcr_d_status": {
"name": "FCR-D Status"
},
"fcr_d_date": {
"name": "FCR-D Date"
},
"charge_peak": {
"name": "Charge Peak"
},
Expand Down Expand Up @@ -194,6 +185,15 @@
},
"cm10_status_date": {
"name": "Status Date"
},
"fcr_d_info": {
"name": "FCR-D Info"
},
"fcr_d_status": {
"name": "FCR-D Status"
},
"fcr_d_date": {
"name": "FCR-D Date"
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions custom_components/checkwatt/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,6 @@
"next_update": {
"name": "Next update"
},
"fcr_d_state": {
"name": "FCR-D State"
},
"fcr_d_status": {
"name": "FCR-D Status"
},
"fcr_d_date": {
"name": "FCR-D Date"
},
"charge_peak": {
"name": "Charge Peak"
},
Expand Down Expand Up @@ -194,6 +185,15 @@
},
"cm10_status_date": {
"name": "Status Date"
},
"fcr_d_info": {
"name": "FCR-D Info"
},
"fcr_d_status": {
"name": "FCR-D Status"
},
"fcr_d_date": {
"name": "FCR-D Date"
}
}
}
Expand Down
Loading

0 comments on commit 12335f9

Please sign in to comment.