Collection of CheckMK checks (see https://checkmk.com/). All checks and plugins are provided as is. Absolutely no warranty. Send any comments to thl-cmk[at]outlook[dot]com

Skip to content
Snippets Groups Projects
Commit 3e2241b0 authored by thl-cmk's avatar thl-cmk :flag_na:
Browse files

update project

parent 27c744a9
No related branches found
No related tags found
No related merge requests found
Showing
with 226 additions and 208 deletions
[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.
......
File added
......@@ -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):
......
......@@ -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 = {
......
......@@ -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):
......
......@@ -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(
......
......@@ -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):
......
......@@ -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(
......
......@@ -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: '
......
......@@ -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(
......
......@@ -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:
......
......@@ -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
......@@ -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}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment