From 3e2241b082d8dcc82fa899918928d754a45c4e08 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Fri, 5 Jan 2024 23:16:41 +0100 Subject: [PATCH] update project --- README.md | 2 +- mkp/fritzbox_smarthome-0.8.5-20240105.mkp | Bin 0 -> 14253 bytes .../fritzbox_smarthome_app_lock.py | 4 +- .../agent_based/fritzbox_smarthome_battery.py | 5 +- .../fritzbox_smarthome_device_lock.py | 5 +- .../fritzbox_smarthome_power_meter.py | 50 +++--- .../fritzbox_smarthome_power_socket.py | 5 +- .../agent_based/fritzbox_smarthome_switch.py | 22 +-- .../fritzbox_smarthome_temperature.py | 16 +- .../fritzbox_smarthome_thermostat.py | 125 +++++++------- .../agent_based/utils/fritzbox_smarthome.py | 161 +++++++++--------- source/gui/metrics/fritzbox_smarthome.py | 35 ++++ source/packages/fritzbox_smarthome | 4 +- 13 files changed, 226 insertions(+), 208 deletions(-) create mode 100644 mkp/fritzbox_smarthome-0.8.5-20240105.mkp diff --git a/README.md b/README.md index e0a1874..0b5890b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.8.4-20231231.mkp "fritzbox_smarthome-0.8.4-20231231.mkp" +[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.8.5-20240105.mkp "fritzbox_smarthome-0.8.5-20240105.mkp" # AVM Fritz!Box Smarthome This repository contains a additional check_MK Fritz!Box Agent which can gather informations over the AVM AHA HTTP Interface about SmartHome Devices connected to an Fritz!Box. diff --git a/mkp/fritzbox_smarthome-0.8.5-20240105.mkp b/mkp/fritzbox_smarthome-0.8.5-20240105.mkp new file mode 100644 index 0000000000000000000000000000000000000000..441d4a8ccd116c6f305d7bd5294d679a8aa95c4f GIT binary patch literal 14253 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 literal 0 HcmV?d00001 diff --git a/source/agent_based/fritzbox_smarthome_app_lock.py b/source/agent_based/fritzbox_smarthome_app_lock.py index b52314e..8582225 100644 --- a/source/agent_based/fritzbox_smarthome_app_lock.py +++ b/source/agent_based/fritzbox_smarthome_app_lock.py @@ -42,9 +42,7 @@ def discovery_fritzbox_smarthome_app_lock_multiple( def check_fritzbox_smarthome_app_lock_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): - return - if section.lock is None: + if not isinstance(section, AvmSmartHomeDevice) or section.lock is None: return def _get_status(status: int): diff --git a/source/agent_based/fritzbox_smarthome_battery.py b/source/agent_based/fritzbox_smarthome_battery.py index 61d597b..57f3556 100644 --- a/source/agent_based/fritzbox_smarthome_battery.py +++ b/source/agent_based/fritzbox_smarthome_battery.py @@ -42,10 +42,7 @@ def discovery_fritzbox_smarthome_battery_multiple( def check_fritzbox_smarthome_battery_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): - return - - if section.battery_low is None: + if not isinstance(section, AvmSmartHomeDevice) or section.battery_low is None: return _battery_low = { diff --git a/source/agent_based/fritzbox_smarthome_device_lock.py b/source/agent_based/fritzbox_smarthome_device_lock.py index 6c088fd..a6e1bc2 100644 --- a/source/agent_based/fritzbox_smarthome_device_lock.py +++ b/source/agent_based/fritzbox_smarthome_device_lock.py @@ -42,10 +42,7 @@ def discovery_fritzbox_smarthome_device_lock_multiple( def check_fritzbox_smarthome_device_lock_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): - return - - if section.device_lock is None: + if not isinstance(section, AvmSmartHomeDevice) or section.device_lock is None: return def _get_status(status: int): diff --git a/source/agent_based/fritzbox_smarthome_power_meter.py b/source/agent_based/fritzbox_smarthome_power_meter.py index 6e1c096..9b0f610 100644 --- a/source/agent_based/fritzbox_smarthome_power_meter.py +++ b/source/agent_based/fritzbox_smarthome_power_meter.py @@ -15,6 +15,7 @@ from typing import Dict from cmk.base.plugins.agent_based.agent_based_api.v1 import ( GetRateError, + Metric, Result, Service, State, @@ -32,7 +33,7 @@ def discovery_fritzbox_smarthome_voltage_single( section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> DiscoveryResult: if isinstance(section, AvmSmartHomeDevice): - if section.power_meter and section.power_meter.voltage: + if section.power_meter and section.power_meter.voltage is not None: yield Service() @@ -41,7 +42,7 @@ def discovery_fritzbox_smarthome_voltage_multiple( ) -> DiscoveryResult: if not isinstance(section, AvmSmartHomeDevice): for device_id, device in section.items(): - if device.power_meter and device.power_meter.voltage: + if device.power_meter and device.power_meter.voltage is not None: yield Service(item=str(device_id)) @@ -50,7 +51,7 @@ def check_fritzbox_smarthome_voltage_single( ) -> CheckResult: if not isinstance(section, AvmSmartHomeDevice): return - if section.power_meter and section.power_meter.voltage: + if section.power_meter and section.power_meter.voltage is not None: yield from check_levels( label='Voltage', levels_lower=params.get('levels_lower'), @@ -96,7 +97,7 @@ def discovery_fritzbox_smarthome_power_single( section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> DiscoveryResult: if isinstance(section, AvmSmartHomeDevice): - if section.power_meter and section.power_meter.power: + if section.power_meter and section.power_meter.power is not None: yield Service() @@ -105,7 +106,7 @@ def discovery_fritzbox_smarthome_power_multiple( ) -> DiscoveryResult: if not isinstance(section, AvmSmartHomeDevice): for device_id, device in section.items(): - if device.power_meter and device.power_meter.power: + if device.power_meter and device.power_meter.power is not None: yield Service(item=str(device_id)) @@ -115,7 +116,7 @@ def check_fritzbox_smarthome_power_single( if not isinstance(section, AvmSmartHomeDevice): return - if section.power_meter and section.power_meter.power: + if section.power_meter and section.power_meter.power is not None: yield from check_levels( value=section.power_meter.power, metric_name='power', @@ -161,7 +162,7 @@ def discovery_fritzbox_smarthome_energy_single( section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> DiscoveryResult: if isinstance(section, AvmSmartHomeDevice): - if section.power_meter and section.power_meter.energy: + if section.power_meter and section.power_meter.energy is not None: yield Service() @@ -170,7 +171,7 @@ def discovery_fritzbox_smarthome_energy_multiple( ) -> DiscoveryResult: if not isinstance(section, AvmSmartHomeDevice): for device_id, device in section.items(): - if device.power_meter and device.power_meter.energy: + if device.power_meter and device.power_meter.energy is not None: yield Service(item=str(device_id)) @@ -180,32 +181,37 @@ def check_fritzbox_smarthome_energy_single( if not isinstance(section, AvmSmartHomeDevice): return - if section.power_meter and section.power_meter.energy: - try: - energy = get_rate( - value_store=get_value_store(), - key='energy', - time=time_now(), - value=section.power_meter.energy, - raise_overflow=True - ) - except GetRateError as e: - yield Result(state=State.OK, notice=str(e)) + if section.power_meter and section.power_meter.power is not None: + value_store = get_value_store() + if not (last_timestamp := value_store.get('last_timestamp')): + value_store['last_timestamp'] = time_now() else: + time_span = time_now() - last_timestamp + value_store['last_timestamp'] = time_now() + energy = section.power_meter.power / 3600 * time_span + yield from check_levels( value=energy, - metric_name='energy', + metric_name='energy_current', label='Consumption current', render_func=lambda x: physical_precision(v=x, precision=3, unit_symbol="Wh"), levels_lower=params.get('levels_lower'), levels_upper=params.get('levels_upper'), ) + yield Result( + state=State.OK, + notice=f'Consumption current is an estimation, ' + f'assuming constant power usage of {section.power_meter.power}W ' + f'for the last {time_span:.2f} seconds' + ) + if section.power_meter and section.power_meter.energy is not None: yield Result( state=State.OK, - summary=f'Consumption total: ' - f'{physical_precision(v=section.power_meter.energy, precision=3, unit_symbol="Wh")}' + summary=f'Consumption total' + f': {physical_precision(v=section.power_meter.energy, precision=3, unit_symbol="Wh")}' ) + yield Metric(name='energy_total', value=section.power_meter.energy) def check_fritzbox_smarthome_energy_multiple( diff --git a/source/agent_based/fritzbox_smarthome_power_socket.py b/source/agent_based/fritzbox_smarthome_power_socket.py index d702cb9..3a96807 100644 --- a/source/agent_based/fritzbox_smarthome_power_socket.py +++ b/source/agent_based/fritzbox_smarthome_power_socket.py @@ -42,10 +42,7 @@ def discovery_fritzbox_smarthome_power_socket_multiple( def check_fritzbox_smarthome_power_socket_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): - return - - if not section.switch: + if not isinstance(section, AvmSmartHomeDevice) or not section.switch: return def _get_status(status: int): diff --git a/source/agent_based/fritzbox_smarthome_switch.py b/source/agent_based/fritzbox_smarthome_switch.py index 7bfeac1..8700754 100644 --- a/source/agent_based/fritzbox_smarthome_switch.py +++ b/source/agent_based/fritzbox_smarthome_switch.py @@ -26,7 +26,7 @@ def discovery_fritzbox_smarthome_switch_single( section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> DiscoveryResult: if isinstance(section, AvmSmartHomeDevice): - if section.switch is not None: + if section.simple_on_off is not None: yield Service() @@ -42,21 +42,17 @@ def discovery_fritzbox_smarthome_switch_multiple( def check_fritzbox_smarthome_switch_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): - return - - if not section.simple_on_off: + if not isinstance(section, AvmSmartHomeDevice) or section.simple_on_off is None: return - if section.simple_on_off: - def _get_status(status: int): - _simple_onf_off_state = { - 0: 'off', - 1: 'on', - } - return _simple_onf_off_state.get(status, f'unknown ({status})') + def _get_status(status: int): + _simple_onf_off_state = { + 0: 'off', + 1: 'on', + } + return _simple_onf_off_state.get(status, f'unknown ({status})') - yield Result(state=State.OK, summary=f'State: {_get_status(section.switch.state)}') + yield Result(state=State.OK, summary=f'State: {_get_status(section.switch.state)}') def check_fritzbox_smarthome_switch_multiple( diff --git a/source/agent_based/fritzbox_smarthome_temperature.py b/source/agent_based/fritzbox_smarthome_temperature.py index 42875b7..86ebd47 100644 --- a/source/agent_based/fritzbox_smarthome_temperature.py +++ b/source/agent_based/fritzbox_smarthome_temperature.py @@ -38,17 +38,15 @@ def discovery_fritzbox_smarthome_temperature_multiple( def check_fritzbox_smarthome_temperature_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): - return - - if not section.temperature: + if not isinstance(section, AvmSmartHomeDevice) or not section.temperature: return - yield from check_temperature( - reading=section.temperature.celsius, - params=params, - ) - if section.temperature.offset != 0: + if section.temperature.celsius: + yield from check_temperature( + reading=section.temperature.celsius, + params=params, + ) + if section.temperature.offset: _status = section.temperature.celsius + section.temperature.offset * -1 _message = ( f'Temperature measured at the thermostat: ' diff --git a/source/agent_based/fritzbox_smarthome_thermostat.py b/source/agent_based/fritzbox_smarthome_thermostat.py index 2579f61..fcd6244 100644 --- a/source/agent_based/fritzbox_smarthome_thermostat.py +++ b/source/agent_based/fritzbox_smarthome_thermostat.py @@ -46,74 +46,73 @@ def discovery_fritzbox_smarthome_thermostat_multiple( def check_fritzbox_smarthome_thermostat_single( params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] ) -> CheckResult: - if not isinstance(section, AvmSmartHomeDevice): + if not isinstance(section, AvmSmartHomeDevice) or not (thermostat := section.thermostat): return - if not section.thermostat: + if not thermostat.temp_current: return - if thermostat := section.thermostat: - _error_codes = { - 0: 'no error', - 1: 'No adaptation possible. Is the thermostat correctly mounted on the radiator?', - 2: 'Valve stroke too short or battery power too low. Open and close the ' - 'valve tappet several times by hand or insert new batteries.', - 3: 'No valve movement possible. Valve tappet free?', - 4: 'The installation is currently being prepared.', - 5: 'The thermostat is in installation mode and can be mounted on the heating valve.', - 6: 'The thermostat now adapts to the stroke of the heating valve', - } - - if thermostat.temp_target == 126.5: # == radiator off - yield Result(state=State.OK, summary=f'Temperature current: {thermostat.temp_current}°C') - yield Result(state=State.OK, summary=f'Temperature target: radiator off') - else: - deviation = thermostat.temp_current - thermostat.temp_target - if deviation == 0: - yield Result(state=State.OK, summary=f'Temperature current: {thermostat.temp_target}°C') - else: - _message = f'Temperature current: {thermostat.temp_current}°C (deviation from target {deviation}°C)' - _state = State.OK - if params.get('deviation'): - warn, crit = params['deviation'] - if abs(deviation) >= crit: - _state = State.CRIT - elif abs(deviation) >= warn: - _state = State.WARN - yield Result(state=_state, summary=_message) - - yield Result( - state=State.OK, - summary=f'Target: {thermostat.temp_target}°C', - details=f'Temperature target: {thermostat.temp_target}°C' - ) - yield Metric(name='temp_target', value=thermostat.temp_target) - - yield Result(state=State.OK, notice=f'Temperature economic: {thermostat.temp_economic}°C') - yield Result(state=State.OK, notice=f'Temperature comfort: {thermostat.temp_comfort}°C') - - yield Metric(name='temp_current', value=thermostat.temp_current) - yield Metric(name='temp_comfort', value=thermostat.temp_comfort) - yield Metric(name='temp_economic', value=thermostat.temp_economic) - - if thermostat.next_change: - yield Result( - state=State.OK, - notice=f'End of period: {strftime(_TIME_FORMAT, localtime(thermostat.next_change.end_period))}' - ) - yield Result( - state=State.OK, - notice=f'Temperature target after end of period: {thermostat.next_change.temp_change_to}°C' - ) - - _message = f'Error code: {_error_codes.get(thermostat.error_code, f"unknown error {thermostat.error_code}")}' - if thermostat.error_code == 0: - yield Result(state=State.OK, notice=_message) + _error_codes = { + 0: 'no error', + 1: 'No adaptation possible. Is the thermostat correctly mounted on the radiator?', + 2: 'Valve stroke too short or battery power too low. Open and close the ' + 'valve tappet several times by hand or insert new batteries.', + 3: 'No valve movement possible. Valve tappet free?', + 4: 'The installation is currently being prepared.', + 5: 'The thermostat is in installation mode and can be mounted on the heating valve.', + 6: 'The thermostat now adapts to the stroke of the heating valve', + } + + if thermostat.temp_target == 126.5: # == radiator off + yield Result(state=State.OK, summary=f'Temperature current: {thermostat.temp_current}°C') + yield Result(state=State.OK, summary=f'Temperature target: radiator off') + else: + deviation = thermostat.temp_current - thermostat.temp_target + if deviation == 0: + yield Result(state=State.OK, summary=f'Temperature current: {thermostat.temp_target}°C') else: - yield Result( - state=State(params.get('state_on_error', 1)), - summary=f'Error Code: {thermostat.error_code} (see details)', - details=_message) + _message = f'Temperature current: {thermostat.temp_current}°C (deviation from target {deviation}°C)' + _state = State.OK + if params.get('deviation'): + warn, crit = params['deviation'] + if abs(deviation) >= crit: + _state = State.CRIT + elif abs(deviation) >= warn: + _state = State.WARN + yield Result(state=_state, summary=_message) + + yield Result( + state=State.OK, + summary=f'Target: {thermostat.temp_target}°C', + details=f'Temperature target: {thermostat.temp_target}°C' + ) + yield Metric(name='temp_target', value=thermostat.temp_target) + + yield Result(state=State.OK, notice=f'Temperature economic: {thermostat.temp_economic}°C') + yield Result(state=State.OK, notice=f'Temperature comfort: {thermostat.temp_comfort}°C') + + yield Metric(name='temp_current', value=thermostat.temp_current) + yield Metric(name='temp_comfort', value=thermostat.temp_comfort) + yield Metric(name='temp_economic', value=thermostat.temp_economic) + + if thermostat.next_change: + yield Result( + state=State.OK, + notice=f'End of period: {strftime(_TIME_FORMAT, localtime(thermostat.next_change.end_period))}' + ) + yield Result( + state=State.OK, + notice=f'Temperature target after end of period: {thermostat.next_change.temp_change_to}°C' + ) + + _message = f'Error code: {_error_codes.get(thermostat.error_code, f"unknown error {thermostat.error_code}")}' + if thermostat.error_code == 0: + yield Result(state=State.OK, notice=_message) + else: + yield Result( + state=State(params.get('state_on_error', 1)), + summary=f'Error Code: {thermostat.error_code} (see details)', + details=_message) def check_fritzbox_smarthome_thermostat_multiple( diff --git a/source/agent_based/utils/fritzbox_smarthome.py b/source/agent_based/utils/fritzbox_smarthome.py index b6a9137..25c4b8b 100644 --- a/source/agent_based/utils/fritzbox_smarthome.py +++ b/source/agent_based/utils/fritzbox_smarthome.py @@ -18,49 +18,35 @@ from typing import Any, List, Dict @dataclass(frozen=True) class AvmTemperature: - celsius: float - offset: float - - -@dataclass(frozen=True) -class AvmAlert: - last_alert_chg_timestamp: int - state: int - - -@dataclass(frozen=True) -class AvmButton: - last_pressed_timestamp: int - id: int | None = None - identifier: int | None = None - name: str | None = None + celsius: float | None + offset: float | None @dataclass(frozen=True) class AvmPowerMeter: - energy: float - power: float - voltage: float + energy: float | None + power: float | None + voltage: float | None @dataclass(frozen=True) class AvmSimpleOnOff: - state: int + state: int | None @dataclass(frozen=True) class AvmNextChange: - end_period: int - temp_change_to: float + end_period: int | None + temp_change_to: float | None @dataclass(frozen=True) class AvmThermostat: - error_code: int - temp_comfort: float - temp_current: float - temp_economic: float - temp_target: float + error_code: int | None + temp_comfort: float | None + temp_current: float | None + temp_economic: float | None + temp_target: float | None adaptive_heating_active: int | None = None adaptive_heating_running: int | None = None battery: float | None = None @@ -75,12 +61,12 @@ class AvmThermostat: @dataclass(frozen=True) class AvmSwitch: mode: str - state: int + state: int | None @dataclass(frozen=True) class AvmSmartHomeDevice: - fbm: int + fbm: int | None functions: List[str] fw_version: str id: str @@ -111,87 +97,96 @@ _AVM_NEXT_CHANGE = 'nextchange' def _get_battery_low(device: Dict[str, Any]) -> int | None: try: return int(device[_AVM_THERMOSTAT]['batterylow']) - except KeyError: + except (KeyError, ValueError): pass - return None - def _get_lock(device: Dict[str, Any]) -> int | None: try: return int(device[_AVM_THERMOSTAT]['lock']) - except KeyError: + except (KeyError, ValueError): pass try: return int(device[_AVM_SWITCH]['lock']) - except KeyError: + except (KeyError, ValueError): pass - return None - def _get_device_lock(device: Dict[str, Any]) -> int | None: try: return int(device[_AVM_THERMOSTAT]['devicelock']) - except KeyError: + except (KeyError, ValueError): pass try: return int(device[_AVM_SWITCH]['devicelock']) - except KeyError: + except (KeyError, ValueError): pass - return None + +def _get_int(value: str | None) -> int | None: + if value is not None and value.isdigit(): + return int(value) + + +def _get_float(value: str | None, scale: float = 1.0) -> float | None: + if value is not None and value.isdigit(): + return float(value) / scale def parse_avm_smarthome_device(raw_device: Dict[str, Any]) -> AvmSmartHomeDevice: return AvmSmartHomeDevice( - battery_low=_get_battery_low(raw_device), - device_lock=_get_device_lock(raw_device), - fbm=int(raw_device['functionbitmask']), - functions=get_avm_device_functions_from_fbm(int(raw_device['functionbitmask'])), - fw_version=str(raw_device['fwversion']), - id=str(raw_device['id']), - identifier=str(raw_device['identifier']), - lock=_get_lock(raw_device), - manufacturer=str(raw_device['manufacturer']), - name=str(raw_device['name']), - present=int(raw_device['present']), - product_name=str(raw_device['productname']), - tx_busy=int(raw_device['txbusy']) if raw_device.get('txbusy') else None, - temperature=AvmTemperature( - celsius=float(raw_device[_AVM_TEMPERATURE]['celsius']) / 10.0, - offset=float(raw_device[_AVM_TEMPERATURE]['offset']) / 10.0, - ) if raw_device.get(_AVM_TEMPERATURE) else None, - thermostat=AvmThermostat( - temp_current=float(raw_device[_AVM_THERMOSTAT]['tist']) / 2.0, - temp_target=float(raw_device[_AVM_THERMOSTAT]['tsoll']) / 2.0, - temp_economic=float(raw_device[_AVM_THERMOSTAT]['absenk']) / 2.0, - temp_comfort=float(raw_device[_AVM_THERMOSTAT]['komfort']) / 2.0, - error_code=int(raw_device[_AVM_THERMOSTAT]['errorcode']), - next_change=AvmNextChange( - end_period=int(raw_device[_AVM_THERMOSTAT][_AVM_NEXT_CHANGE]['endperiod']), - temp_change_to=float(raw_device[_AVM_THERMOSTAT][_AVM_NEXT_CHANGE]['tchange']) / 2.0, - ) if raw_device[_AVM_THERMOSTAT].get(_AVM_NEXT_CHANGE) else None, - ) if raw_device.get(_AVM_THERMOSTAT) else None, - switch=AvmSwitch( - state=int(raw_device[_AVM_SWITCH]['state']), - mode=str(raw_device[_AVM_SWITCH]['mode']), - ) if raw_device.get(_AVM_SWITCH) else None, - power_meter=AvmPowerMeter( - voltage=float(raw_device[_AVM_POWER_METER]['voltage']) / 1000, - power=float(raw_device[_AVM_POWER_METER]['power']) / 1000, - energy=float(raw_device[_AVM_POWER_METER]['energy']), # / 1000, - ) if raw_device.get(_AVM_POWER_METER) else None, - simple_on_off=AvmSimpleOnOff( - state=int(raw_device[_AVM_SIMPLE_ON_OFF]['state']), - ) if raw_device.get(_AVM_SIMPLE_ON_OFF) else None, - ) - - -def get_avm_device_functions_from_fbm(fbm: int) -> List[str]: + battery_low=_get_battery_low(raw_device), + device_lock=_get_device_lock(raw_device), + fbm=_get_int(raw_device.get('functionbitmask')), + functions=get_avm_device_functions_from_fbm(_get_int(raw_device.get('functionbitmask'))), + fw_version=str(raw_device['fwversion']), + id=str(raw_device['id']), + identifier=str(raw_device['identifier']), + lock=_get_lock(raw_device), + manufacturer=str(raw_device['manufacturer']), + name=str(raw_device['name']), + present=_get_int(raw_device.get('present')), + product_name=str(raw_device['productname']), + tx_busy=_get_int(raw_device.get('txbusy')), + temperature=AvmTemperature( + celsius=_get_float(value=raw_device[_AVM_TEMPERATURE].get('celsius'), scale=10.0), + offset=_get_float(value=raw_device[_AVM_TEMPERATURE].get('offset'), scale=10.0), + ) if raw_device.get(_AVM_TEMPERATURE) else None, + thermostat=AvmThermostat( + temp_current=_get_float(value=raw_device[_AVM_THERMOSTAT].get('tist'), scale=2.0), + temp_target=_get_float(value=raw_device[_AVM_THERMOSTAT].get('tsoll'), scale=2.0), + temp_economic=_get_float(value=raw_device[_AVM_THERMOSTAT].get('absenk'), scale=2.0), + temp_comfort=_get_float(value=raw_device[_AVM_THERMOSTAT].get('komfort'), scale=2.0), + error_code=_get_int(value=raw_device[_AVM_THERMOSTAT].get('errorcode')), + next_change=AvmNextChange( + end_period=_get_int(raw_device[_AVM_THERMOSTAT][_AVM_NEXT_CHANGE].get('endperiod')), + temp_change_to=_get_float( + value=raw_device[_AVM_THERMOSTAT][_AVM_NEXT_CHANGE].get('tchange'), scale=2.0 + ), + ) if raw_device[_AVM_THERMOSTAT].get(_AVM_NEXT_CHANGE) else None, + ) if raw_device.get(_AVM_THERMOSTAT) else None, + switch=AvmSwitch( + state=_get_int(raw_device[_AVM_SWITCH].get('state')), + mode=str(raw_device[_AVM_SWITCH]['mode']), + ) if raw_device.get(_AVM_SWITCH) else None, + power_meter=AvmPowerMeter( + voltage=_get_float(raw_device[_AVM_POWER_METER].get('voltage'),1000), + power=_get_float(raw_device[_AVM_POWER_METER].get('power'), 1000), + energy=_get_float(raw_device[_AVM_POWER_METER].get('energy')), # / 1000, + ) if raw_device.get(_AVM_POWER_METER) else None, + simple_on_off=AvmSimpleOnOff( + state=_get_int(raw_device[_AVM_SIMPLE_ON_OFF].get('state')), + ) if raw_device.get(_AVM_SIMPLE_ON_OFF) else None, + ) + + +def get_avm_device_functions_from_fbm(fbm: int | None) -> List[str]: functions = [] + if fbm is None: + return functions + if fbm >> 0 & 1: functions.append('HAN-FUN Device') if fbm >> 2 & 1: diff --git a/source/gui/metrics/fritzbox_smarthome.py b/source/gui/metrics/fritzbox_smarthome.py index 7a8fc12..f7cdc27 100644 --- a/source/gui/metrics/fritzbox_smarthome.py +++ b/source/gui/metrics/fritzbox_smarthome.py @@ -30,6 +30,18 @@ check_metrics["check_mk-fritzbox_smarthome_thermostat_multiple"] = { "temp_comfort": {"auto_graph": False}, } +metric_info["energy_total"] = { + "title": _("Energy total"), + "color": "31/b", + "unit": "wh", +} + +metric_info["energy_current"] = { + "title": _("Energy current"), + "color": "16/b", + "unit": "wh", +} + metric_info["temp_current"] = { "title": _("Temperature current"), "color": "26/a", @@ -51,6 +63,20 @@ metric_info["temp_comfort"] = { "unit": "c", } +graph_info["fritzbox_smart_home_energy_surrent"] = { + "title": "Electrical energy consumption current", + "metrics": [ + ("energy_current", "area") + ] +} + +graph_info["fritzbox_smart_home_energy_total"] = { + "title": "Electrical energy consumption total", + "metrics": [ + ("energy_total", "area") + ] +} + graph_info["fritzbox_smart_home_temp_control"] = { "title": _("Thermostat temperature control"), "metrics": [ @@ -78,3 +104,12 @@ perfometer_info.append(('stacked', [ 'total': 50, } ])) + +perfometer_info.append( + { + "type": "logarithmic", + "metric": "energy_current", + "half_value": 100, + "exponent": 3, + } +) \ No newline at end of file diff --git a/source/packages/fritzbox_smarthome b/source/packages/fritzbox_smarthome index 0d4ae91..4d2081e 100644 --- a/source/packages/fritzbox_smarthome +++ b/source/packages/fritzbox_smarthome @@ -49,7 +49,7 @@ 'plugins/views/fritzbox_smarthome.py']}, 'name': 'fritzbox_smarthome', 'title': 'Fritz!Box SmartHome', - 'version': '0.8.4-20231231', + 'version': '0.8.5-20240105', 'version.min_required': '2.2.0b1', - 'version.packaged': '2.2.0p14', + 'version.packaged': '2.2.0p17', 'version.usable_until': None} -- GitLab