Skip to content

Commit

Permalink
fix: fixed power socket protocol adaptation
Browse files Browse the repository at this point in the history
feat: added power socket protocol description to readme
  • Loading branch information
alryaz committed Sep 14, 2021
1 parent e2913fd commit 964f335
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 80 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,30 @@ hekr:
- main_power
```

## Power socket protocol: `power_socket`

![Loaded switches for power socket protocol](https://raw.githubusercontent.com/alryaz/hass-hekr-component/master/images/power_socket/switches.png)

<!--_(more screenshots available at: [images/power_socket](images/power_socket))_-->

### Supported devices
- CZ-05 (??? Information pending)

### Example configuration
```yaml
hekr:
devices:
- host: tv-power-socket.lan
device_id: ESP_2M_AABBCCDDEEFF
control_key: 202cb962ac59075b964b07152d234b70
protocol: power_socket
```

In this state, the component will generate a single switch (`main_power`) obtained via a
single `Quary` command.

<!-- Unfortunately, they named it Quary, not Query, and I'm equally irked by this -->

## Fetching `device_id` and `control_key` for local setup
The following steps (evidently) assume you already paired target device using Wisen.

Expand Down
232 changes: 152 additions & 80 deletions custom_components/hekr/supported_protocols.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
"""Supported protocols for Hekr devices."""

__all__ = [
'SUPPORTED_PROTOCOLS',
'POWER_METER',
"SUPPORTED_PROTOCOLS",
"POWER_METER",
]

from homeassistant.components.switch import ATTR_CURRENT_POWER_W, DEVICE_CLASS_SWITCH
from homeassistant.const import (
ATTR_NAME, ATTR_ICON, ATTR_UNIT_OF_MEASUREMENT,
POWER_WATT, ENERGY_KILO_WATT_HOUR,
STATE_OK, STATE_PROBLEM, STATE_ON, STATE_OFF, ATTR_STATE,
ATTR_DEVICE_CLASS, DEVICE_CLASS_POWER)
ATTR_NAME,
ATTR_ICON,
ATTR_UNIT_OF_MEASUREMENT,
POWER_WATT,
ENERGY_KILO_WATT_HOUR,
STATE_OK,
STATE_PROBLEM,
STATE_ON,
STATE_OFF,
ATTR_STATE,
ATTR_DEVICE_CLASS,
DEVICE_CLASS_POWER,
)

from hekrapi.protocols.power_meter import (
PROTOCOL as PROTOCOL_POWER_METER,
Expand All @@ -19,65 +28,87 @@
CurrentWarning,
)
from hekrapi.protocols.power_socket import PROTOCOL as PROTOCOL_POWER_SOCKET
from .const import ATTR_MONITORED, PROTOCOL_DETECTION, PROTOCOL_DEFINITION, PROTOCOL_FILTER, \
PROTOCOL_SENSORS, PROTOCOL_CMD_UPDATE, PROTOCOL_DEFAULT, PROTOCOL_SWITCHES, PROTOCOL_PORT, PROTOCOL_NAME, \
PROTOCOL_CMD_RECEIVE, PROTOCOL_CMD_TURN_ON, PROTOCOL_CMD_TURN_OFF, PROTOCOL_MODEL, PROTOCOL_MANUFACTURER
from .const import (
ATTR_MONITORED,
PROTOCOL_DETECTION,
PROTOCOL_DEFINITION,
PROTOCOL_FILTER,
PROTOCOL_SENSORS,
PROTOCOL_CMD_UPDATE,
PROTOCOL_DEFAULT,
PROTOCOL_SWITCHES,
PROTOCOL_PORT,
PROTOCOL_NAME,
PROTOCOL_CMD_RECEIVE,
PROTOCOL_CMD_TURN_ON,
PROTOCOL_CMD_TURN_OFF,
PROTOCOL_MODEL,
PROTOCOL_MANUFACTURER,
)


def power_meter_attribute_filter(attributes: dict) -> dict:
if "current_energy_consumption" in attributes:
attributes["current_energy_consumption"] = round(attributes["current_energy_consumption"] * 1000, 1)
attributes["current_energy_consumption"] = round(
attributes["current_energy_consumption"] * 1000, 1
)

if "total_active_power" in attributes:
attributes["total_active_power"] = round(attributes["total_active_power"] * 1000, 1)
attributes["total_active_power"] = round(
attributes["total_active_power"] * 1000, 1
)

# filter attributes by phase count
if "phase_count" in attributes:
attributes = {
attribute: value
for attribute, value in attributes.items()
if not (attribute[-2:] == '_' and attribute[-1:].isnumeric())
or int(attribute[-1:]) <= attributes['phase_count']
if not (attribute[-2:] == "_" and attribute[-1:].isnumeric())
or int(attribute[-1:]) <= attributes["phase_count"]
}

# get mean current
if "current_1" in attributes:
currents = [
value for attribute, value in attributes.items()
value
for attribute, value in attributes.items()
if attribute[:-1] == "current_"
]
total_current = sum(currents)
attributes['mean_current'] = round(float(total_current) / len(currents), 3)
attributes['total_current'] = total_current
attributes["mean_current"] = round(float(total_current) / len(currents), 3)
attributes["total_current"] = total_current

# get mean voltages
if "voltage_1" in attributes:
voltages = [
value for attribute, value in attributes.items()
value
for attribute, value in attributes.items()
if attribute[:-1] == "voltage_" and value
]
attributes['mean_voltage'] = round(float(sum(voltages)) / len(voltages), 1)
attributes["mean_voltage"] = round(float(sum(voltages)) / len(voltages), 1)

# detect state of the device
attributes["state"] = STATE_OK
if "warning_voltage" in attributes:
if attributes['warning_voltage'] != VoltageWarning.OK:
if attributes["warning_voltage"] != VoltageWarning.OK:
attributes["state"] = STATE_PROBLEM
attributes['warning_voltage'] = attributes['warning_voltage'].name.lower()
attributes["warning_voltage"] = attributes["warning_voltage"].name.lower()

if "warning_battery" in attributes:
if attributes['warning_battery'] != PowerSupplyWarning.OK:
if attributes["warning_battery"] != PowerSupplyWarning.OK:
attributes["state"] = STATE_PROBLEM
attributes["warning_battery"] = attributes["warning_battery"].name.lower()

if "warning_current" in attributes:
if attributes['warning_current'] != CurrentWarning.OK:
if attributes["warning_current"] != CurrentWarning.OK:
attributes["state"] = STATE_PROBLEM
attributes['warning_current'] = attributes['warning_current'].name.lower()
attributes["warning_current"] = attributes["warning_current"].name.lower()

# process switch state
if "switch_state" in attributes:
attributes["switch_state"] = STATE_ON if attributes["switch_state"] else STATE_OFF
attributes["switch_state"] = (
STATE_ON if attributes["switch_state"] else STATE_OFF
)

return attributes

Expand All @@ -92,89 +123,127 @@ def power_meter_attribute_filter(attributes: dict) -> dict:
PROTOCOL_FILTER: power_meter_attribute_filter,
PROTOCOL_SENSORS: {
"general": {
ATTR_NAME: "General Information", ATTR_ICON: 'mdi:eye',
ATTR_STATE: "state", ATTR_MONITORED: True,
PROTOCOL_CMD_UPDATE: 'queryDev',
PROTOCOL_CMD_RECEIVE: 'reportDev',
ATTR_NAME: "General Information",
ATTR_ICON: "mdi:eye",
ATTR_STATE: "state",
ATTR_MONITORED: True,
PROTOCOL_CMD_UPDATE: "queryDev",
PROTOCOL_CMD_RECEIVE: "reportDev",
PROTOCOL_DEFAULT: False,
},
"detailed": {
ATTR_NAME: 'Detailed Information', ATTR_ICON: 'mdi:eye-settings',
ATTR_STATE: "state", ATTR_MONITORED: True,
PROTOCOL_CMD_UPDATE: 'queryData',
PROTOCOL_CMD_RECEIVE: 'reportData',
ATTR_NAME: "Detailed Information",
ATTR_ICON: "mdi:eye-settings",
ATTR_STATE: "state",
ATTR_MONITORED: True,
PROTOCOL_CMD_UPDATE: "queryData",
PROTOCOL_CMD_RECEIVE: "reportData",
PROTOCOL_DEFAULT: False,
},
# queryDev-related sensors
"status": {
ATTR_NAME: "Status", ATTR_ICON: {
STATE_PROBLEM: 'mdi:alert',
STATE_OK: 'mdi:check-circle',
PROTOCOL_DEFAULT: 'mdi:help-circle'
ATTR_NAME: "Status",
ATTR_ICON: {
STATE_PROBLEM: "mdi:alert",
STATE_OK: "mdi:check-circle",
PROTOCOL_DEFAULT: "mdi:help-circle",
},
ATTR_STATE: "state",
ATTR_MONITORED: ["phase_count", "warning_voltage", "warning_current", "warning_battery"],
PROTOCOL_CMD_UPDATE: 'queryDev',
PROTOCOL_CMD_RECEIVE: 'reportDev',
ATTR_MONITORED: [
"phase_count",
"warning_voltage",
"warning_current",
"warning_battery",
],
PROTOCOL_CMD_UPDATE: "queryDev",
PROTOCOL_CMD_RECEIVE: "reportDev",
PROTOCOL_DEFAULT: True,
},
"current_consumption": {
ATTR_NAME: "Current Consumption", ATTR_ICON: 'mdi:gauge',
ATTR_STATE: "current_energy_consumption", ATTR_UNIT_OF_MEASUREMENT: POWER_WATT,
ATTR_NAME: "Current Consumption",
ATTR_ICON: "mdi:gauge",
ATTR_STATE: "current_energy_consumption",
ATTR_UNIT_OF_MEASUREMENT: POWER_WATT,
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
PROTOCOL_CMD_UPDATE: 'queryDev',
PROTOCOL_CMD_RECEIVE: 'reportDev',
PROTOCOL_CMD_UPDATE: "queryDev",
PROTOCOL_CMD_RECEIVE: "reportDev",
PROTOCOL_DEFAULT: True,
},
"total_consumption": {
ATTR_NAME: "Total Consumption", ATTR_ICON: "mdi:sigma",
ATTR_STATE: "total_energy_consumed", ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
PROTOCOL_CMD_UPDATE: 'queryDev',
PROTOCOL_CMD_RECEIVE: 'reportDev',
ATTR_NAME: "Total Consumption",
ATTR_ICON: "mdi:sigma",
ATTR_STATE: "total_energy_consumed",
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
PROTOCOL_CMD_UPDATE: "queryDev",
PROTOCOL_CMD_RECEIVE: "reportDev",
PROTOCOL_DEFAULT: True,
},

# queryData-related sensors
"voltage": {
ATTR_NAME: "Mean Voltage", ATTR_ICON: "mdi:alpha-v-circle",
ATTR_STATE: "mean_voltage", ATTR_UNIT_OF_MEASUREMENT: "V",
ATTR_MONITORED: ["voltage_1", "voltage_2", "voltage_3", "current_frequency"],
PROTOCOL_CMD_UPDATE: 'queryData',
PROTOCOL_CMD_RECEIVE: 'reportData',
ATTR_NAME: "Mean Voltage",
ATTR_ICON: "mdi:alpha-v-circle",
ATTR_STATE: "mean_voltage",
ATTR_UNIT_OF_MEASUREMENT: "V",
ATTR_MONITORED: [
"voltage_1",
"voltage_2",
"voltage_3",
"current_frequency",
],
PROTOCOL_CMD_UPDATE: "queryData",
PROTOCOL_CMD_RECEIVE: "reportData",
PROTOCOL_DEFAULT: False,
},
"current": {
ATTR_NAME: "Total Current", ATTR_ICON: "mdi:alpha-i-circle",
ATTR_STATE: "total_current", ATTR_UNIT_OF_MEASUREMENT: "A",
ATTR_MONITORED: ["mean_current", "current_1", "current_2", "current_3", "current_frequency"],
PROTOCOL_CMD_UPDATE: 'queryData',
PROTOCOL_CMD_RECEIVE: 'reportData',
ATTR_NAME: "Total Current",
ATTR_ICON: "mdi:alpha-i-circle",
ATTR_STATE: "total_current",
ATTR_UNIT_OF_MEASUREMENT: "A",
ATTR_MONITORED: [
"mean_current",
"current_1",
"current_2",
"current_3",
"current_frequency",
],
PROTOCOL_CMD_UPDATE: "queryData",
PROTOCOL_CMD_RECEIVE: "reportData",
PROTOCOL_DEFAULT: False,
},
"power_factor": {
ATTR_NAME: "Power Factor", ATTR_ICON: "mdi:speedometer",
ATTR_STATE: "total_power_factor", ATTR_UNIT_OF_MEASUREMENT: None,
ATTR_NAME: "Power Factor",
ATTR_ICON: "mdi:speedometer",
ATTR_STATE: "total_power_factor",
ATTR_UNIT_OF_MEASUREMENT: None,
ATTR_MONITORED: ["power_factor_1", "power_factor_2", "power_factor_3"],
PROTOCOL_CMD_UPDATE: 'queryData',
PROTOCOL_CMD_RECEIVE: 'reportData',
PROTOCOL_CMD_UPDATE: "queryData",
PROTOCOL_CMD_RECEIVE: "reportData",
PROTOCOL_DEFAULT: False,
},
"active_power": {
ATTR_NAME: "Active Power", ATTR_ICON: "mdi:flash",
ATTR_STATE: "total_active_power", ATTR_UNIT_OF_MEASUREMENT: POWER_WATT,
ATTR_NAME: "Active Power",
ATTR_ICON: "mdi:flash",
ATTR_STATE: "total_active_power",
ATTR_UNIT_OF_MEASUREMENT: POWER_WATT,
ATTR_MONITORED: ["active_power_1", "active_power_2", "active_power_3"],
PROTOCOL_CMD_UPDATE: 'queryData',
PROTOCOL_CMD_RECEIVE: 'reportData',
PROTOCOL_CMD_UPDATE: "queryData",
PROTOCOL_CMD_RECEIVE: "reportData",
PROTOCOL_DEFAULT: False,
},
"reactive_power": {
ATTR_NAME: "Reactive Power", ATTR_ICON: "mdi:flash-outline",
ATTR_STATE: "total_reactive_power", ATTR_UNIT_OF_MEASUREMENT: "kVar",
ATTR_MONITORED: ["reactive_power_1", "reactive_power_2", "reactive_power_3"],
PROTOCOL_CMD_UPDATE: 'queryData',
PROTOCOL_CMD_RECEIVE: 'reportData',
ATTR_NAME: "Reactive Power",
ATTR_ICON: "mdi:flash-outline",
ATTR_STATE: "total_reactive_power",
ATTR_UNIT_OF_MEASUREMENT: "kVar",
ATTR_MONITORED: [
"reactive_power_1",
"reactive_power_2",
"reactive_power_3",
],
PROTOCOL_CMD_UPDATE: "queryData",
PROTOCOL_CMD_RECEIVE: "reportData",
PROTOCOL_DEFAULT: False,
}
},
},
PROTOCOL_SWITCHES: {
"main_power": {
Expand All @@ -183,23 +252,26 @@ def power_meter_attribute_filter(attributes: dict) -> dict:
STATE_ON: "mdi:electric-switch-closed",
PROTOCOL_DEFAULT: "mdi:electric-switch",
},
ATTR_STATE: "switch_state", ATTR_DEVICE_CLASS: DEVICE_CLASS_SWITCH,
ATTR_STATE: "switch_state",
ATTR_DEVICE_CLASS: DEVICE_CLASS_SWITCH,
ATTR_CURRENT_POWER_W: "current_energy_consumption",
PROTOCOL_CMD_UPDATE: 'queryDev',
PROTOCOL_CMD_RECEIVE: 'reportDev',
PROTOCOL_CMD_TURN_ON: ('setSw', {"switch_state": True}),
PROTOCOL_CMD_TURN_OFF: ('setSw', {"switch_state": False}),
PROTOCOL_CMD_UPDATE: "queryDev",
PROTOCOL_CMD_RECEIVE: "reportDev",
PROTOCOL_CMD_TURN_ON: ("setSw", {"switch_state": True}),
PROTOCOL_CMD_TURN_OFF: ("setSw", {"switch_state": False}),
PROTOCOL_DEFAULT: True,
}
}
},
}


def power_socket_attribute_filter(attributes: dict) -> dict:
if "power" in attributes:
attributes["power"] = STATE_ON if attributes["power"] else STATE_OFF

return attributes


POWER_SOCKET = {
PROTOCOL_NAME: "Power Socket",
PROTOCOL_PORT: 10000,
Expand All @@ -217,10 +289,10 @@ def power_socket_attribute_filter(attributes: dict) -> dict:
PROTOCOL_CMD_UPDATE: "Quary",
PROTOCOL_CMD_RECEIVE: "Report",
PROTOCOL_CMD_TURN_ON: ("SetPower", {"power": True}),
PROTOCOL_CMD_TURN_ON: ("SetPower", {"power": False}),
PROTOCOL_CMD_TURN_OFF: ("SetPower", {"power": False}),
PROTOCOL_DEFAULT: True,
}
}
},
}

SUPPORTED_PROTOCOLS = {
Expand Down

0 comments on commit 964f335

Please sign in to comment.