diff --git a/README.md b/README.md index e0a18745466e3e508e30f99959e64ef9f7675736..0b5890b1ba33c30f23b7788e01d126c784a0febd 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 Binary files /dev/null and b/mkp/fritzbox_smarthome-0.8.5-20240105.mkp differ diff --git a/source/agent_based/fritzbox_smarthome_app_lock.py b/source/agent_based/fritzbox_smarthome_app_lock.py index b52314e868c9feb5c542d517f7c46be96c46b179..858222507c979b852cece205f059c8eca4656660 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 61d597b61e08a8b772b93bc69a952ca0ef4cd051..57f355669d3b1c01787d81659ad6df845605ed75 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 6c088fd7a7b41578eb49c70221a78d1b59725bd1..a6e1bc260ff10d6a768fe10eb8bc74ac52de57ea 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 6e1c0968b4c9cf0b14806af2775b0695eecd1311..9b0f610d2fb131b30128147699d2bea345332a0a 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 d702cb982da21eb64265129736411316c179fc0d..3a96807994b16ed557fd397e1441437ba78f8307 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 7bfeac10cc0d0b5123851487a22971cc57557b52..870075455fcb165baf3ecbefd51d6e13622896c2 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 42875b7c17593e59a6768cede99a60a3133aa6b4..86ebd473f2b7f45ab9c3bcee70719873cbceb1f1 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 2579f61cc82f0cece617d8049186e270016959b9..fcd62447e322d8ce81531bb0f9a3e78c04d1c123 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 b6a9137d4fd0e3e03c0ef29975b1cec5f622fc9c..25c4b8b25b5d0a0ec58a0aee4eac5734a9c524cf 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 7a8fc1255de4954df05fb55b76f73ae7034464d6..f7cdc2737576583d6770ba148ba4e38d2bdaf05b 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 0d4ae9128d222eb8b48bd52887620f27e8f36011..4d2081e389b21d34642d14c0f251413e3dc4aae1 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}