mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Add support for homekit_controller secondary entities like power usage (#44013)
This commit is contained in:
parent
f53a83e084
commit
3b0a440770
@ -183,6 +183,21 @@ class AccessoryEntity(HomeKitEntity):
|
||||
return f"homekit-{serial}-aid:{self._aid}"
|
||||
|
||||
|
||||
class CharacteristicEntity(HomeKitEntity):
|
||||
"""
|
||||
A HomeKit entity that is related to an single characteristic rather than a whole service.
|
||||
|
||||
This is typically used to expose additional sensor, binary_sensor or number entities that don't belong with
|
||||
the service entity.
|
||||
"""
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return the ID of this device."""
|
||||
serial = self.accessory_info.value(CharacteristicsTypes.SERIAL_NUMBER)
|
||||
return f"homekit-{serial}-aid:{self._aid}-sid:{self._iid}-cid:{self._iid}"
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Set up a HomeKit connection on a config entry."""
|
||||
conn = HKDevice(hass, entry, entry.data)
|
||||
|
@ -15,7 +15,13 @@ from aiohomekit.model.services import ServicesTypes
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
|
||||
from .const import CONTROLLER, DOMAIN, ENTITY_MAP, HOMEKIT_ACCESSORY_DISPATCH
|
||||
from .const import (
|
||||
CHARACTERISTIC_PLATFORMS,
|
||||
CONTROLLER,
|
||||
DOMAIN,
|
||||
ENTITY_MAP,
|
||||
HOMEKIT_ACCESSORY_DISPATCH,
|
||||
)
|
||||
from .device_trigger import async_fire_triggers, async_setup_triggers_for_entry
|
||||
|
||||
DEFAULT_SCAN_INTERVAL = datetime.timedelta(seconds=60)
|
||||
@ -82,6 +88,9 @@ class HKDevice:
|
||||
# A list of callbacks that turn HK service metadata into entities
|
||||
self.listeners = []
|
||||
|
||||
# A list of callbacks that turn HK characteristics into entities
|
||||
self.char_factories = []
|
||||
|
||||
# The platorms we have forwarded the config entry so far. If a new
|
||||
# accessory is added to a bridge we may have to load additional
|
||||
# platforms. We don't want to load all platforms up front if its just
|
||||
@ -306,6 +315,22 @@ class HKDevice:
|
||||
self.entities.append((accessory.aid, None))
|
||||
break
|
||||
|
||||
def add_char_factory(self, add_entities_cb):
|
||||
"""Add a callback to run when discovering new entities for accessories."""
|
||||
self.char_factories.append(add_entities_cb)
|
||||
self._add_new_entities_for_char([add_entities_cb])
|
||||
|
||||
def _add_new_entities_for_char(self, handlers):
|
||||
for accessory in self.entity_map.accessories:
|
||||
for service in accessory.services:
|
||||
for char in service.characteristics:
|
||||
for handler in handlers:
|
||||
if (accessory.aid, service.iid, char.iid) in self.entities:
|
||||
continue
|
||||
if handler(char):
|
||||
self.entities.append((accessory.aid, service.iid, char.iid))
|
||||
break
|
||||
|
||||
def add_listener(self, add_entities_cb):
|
||||
"""Add a callback to run when discovering new entities for services."""
|
||||
self.listeners.append(add_entities_cb)
|
||||
@ -315,6 +340,7 @@ class HKDevice:
|
||||
"""Process the entity map and create HA entities."""
|
||||
self._add_new_entities(self.listeners)
|
||||
self._add_new_entities_for_accessory(self.accessory_factories)
|
||||
self._add_new_entities_for_char(self.char_factories)
|
||||
|
||||
def _add_new_entities(self, callbacks):
|
||||
for accessory in self.entity_map.accessories:
|
||||
@ -331,26 +357,33 @@ class HKDevice:
|
||||
self.entities.append((aid, iid))
|
||||
break
|
||||
|
||||
async def async_load_platform(self, platform):
|
||||
"""Load a single platform idempotently."""
|
||||
if platform in self.platforms:
|
||||
return
|
||||
|
||||
self.platforms.add(platform)
|
||||
try:
|
||||
await self.hass.config_entries.async_forward_entry_setup(
|
||||
self.config_entry, platform
|
||||
)
|
||||
except Exception:
|
||||
self.platforms.remove(platform)
|
||||
raise
|
||||
|
||||
async def async_load_platforms(self):
|
||||
"""Load any platforms needed by this HomeKit device."""
|
||||
for accessory in self.accessories:
|
||||
for service in accessory["services"]:
|
||||
stype = ServicesTypes.get_short(service["type"].upper())
|
||||
if stype not in HOMEKIT_ACCESSORY_DISPATCH:
|
||||
continue
|
||||
if stype in HOMEKIT_ACCESSORY_DISPATCH:
|
||||
platform = HOMEKIT_ACCESSORY_DISPATCH[stype]
|
||||
await self.async_load_platform(platform)
|
||||
|
||||
platform = HOMEKIT_ACCESSORY_DISPATCH[stype]
|
||||
if platform in self.platforms:
|
||||
continue
|
||||
|
||||
self.platforms.add(platform)
|
||||
try:
|
||||
await self.hass.config_entries.async_forward_entry_setup(
|
||||
self.config_entry, platform
|
||||
)
|
||||
except Exception:
|
||||
self.platforms.remove(platform)
|
||||
raise
|
||||
for char in service["characteristics"]:
|
||||
if char["type"].upper() in CHARACTERISTIC_PLATFORMS:
|
||||
platform = CHARACTERISTIC_PLATFORMS[char["type"].upper()]
|
||||
await self.async_load_platform(platform)
|
||||
|
||||
async def async_update(self, now=None):
|
||||
"""Poll state of all entities attached to this bridge/accessory."""
|
||||
|
@ -1,4 +1,6 @@
|
||||
"""Constants for the homekit_controller component."""
|
||||
from aiohomekit.model.characteristics import CharacteristicsTypes
|
||||
|
||||
DOMAIN = "homekit_controller"
|
||||
|
||||
KNOWN_DEVICES = f"{DOMAIN}-devices"
|
||||
@ -40,3 +42,7 @@ HOMEKIT_ACCESSORY_DISPATCH = {
|
||||
"valve": "switch",
|
||||
"camera-rtp-stream-management": "camera",
|
||||
}
|
||||
|
||||
CHARACTERISTIC_PLATFORMS = {
|
||||
CharacteristicsTypes.Vendor.KOOGEEK_REALTIME_ENERGY: "sensor",
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ from homeassistant.const import (
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
@ -14,7 +15,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import KNOWN_DEVICES, HomeKitEntity
|
||||
from . import KNOWN_DEVICES, CharacteristicEntity, HomeKitEntity
|
||||
|
||||
HUMIDITY_ICON = "mdi:water-percent"
|
||||
TEMP_C_ICON = "mdi:thermometer"
|
||||
@ -22,6 +23,22 @@ BRIGHTNESS_ICON = "mdi:brightness-6"
|
||||
CO2_ICON = "mdi:molecule-co2"
|
||||
|
||||
|
||||
SIMPLE_SENSOR = {
|
||||
CharacteristicsTypes.Vendor.EVE_ENERGY_WATT: {
|
||||
"name": "Real Time Energy",
|
||||
"device_class": DEVICE_CLASS_POWER,
|
||||
"unit": "watts",
|
||||
"icon": "mdi:chart-line",
|
||||
},
|
||||
CharacteristicsTypes.Vendor.KOOGEEK_REALTIME_ENERGY: {
|
||||
"name": "Real Time Energy",
|
||||
"device_class": DEVICE_CLASS_POWER,
|
||||
"unit": "watts",
|
||||
"icon": "mdi:chart-line",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class HomeKitHumiditySensor(HomeKitEntity):
|
||||
"""Representation of a Homekit humidity sensor."""
|
||||
|
||||
@ -216,6 +233,66 @@ class HomeKitBatterySensor(HomeKitEntity):
|
||||
return self.service.value(CharacteristicsTypes.BATTERY_LEVEL)
|
||||
|
||||
|
||||
class SimpleSensor(CharacteristicEntity):
|
||||
"""
|
||||
A simple sensor for a single characteristic.
|
||||
|
||||
This may be an additional secondary entity that is part of another service. An
|
||||
example is a switch that has an energy sensor.
|
||||
|
||||
These *have* to have a different unique_id to the normal sensors as there could
|
||||
be multiple entities per HomeKit service (this was not previously the case).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conn,
|
||||
info,
|
||||
char,
|
||||
device_class=None,
|
||||
unit=None,
|
||||
icon=None,
|
||||
name=None,
|
||||
):
|
||||
"""Initialise a secondary HomeKit characteristic sensor."""
|
||||
self._device_class = device_class
|
||||
self._unit = unit
|
||||
self._icon = icon
|
||||
self._name = name
|
||||
self._char = char
|
||||
|
||||
super().__init__(conn, info)
|
||||
|
||||
def get_characteristic_types(self):
|
||||
"""Define the homekit characteristics the entity is tracking."""
|
||||
return [self._char.type]
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return units for the sensor."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return units for the sensor."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the sensor icon."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the device if any."""
|
||||
return f"{super().name} - {self._name}"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the current sensor value."""
|
||||
return self._char.value
|
||||
|
||||
|
||||
ENTITY_TYPES = {
|
||||
ServicesTypes.HUMIDITY_SENSOR: HomeKitHumiditySensor,
|
||||
ServicesTypes.TEMPERATURE_SENSOR: HomeKitTemperatureSensor,
|
||||
@ -240,3 +317,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
return True
|
||||
|
||||
conn.add_listener(async_add_service)
|
||||
|
||||
@callback
|
||||
def async_add_characteristic(char):
|
||||
kwargs = SIMPLE_SENSOR.get(char.type)
|
||||
if not kwargs:
|
||||
return False
|
||||
info = {"aid": char.service.accessory.aid, "iid": char.service.iid}
|
||||
async_add_entities([SimpleSensor(conn, info, char, **kwargs)], True)
|
||||
|
||||
return True
|
||||
|
||||
conn.add_char_factory(async_add_characteristic)
|
||||
|
@ -0,0 +1,51 @@
|
||||
"""Make sure that existing Koogeek P1EU support isn't broken."""
|
||||
|
||||
from tests.components.homekit_controller.common import (
|
||||
Helper,
|
||||
setup_accessories_from_file,
|
||||
setup_test_accessories,
|
||||
)
|
||||
|
||||
|
||||
async def test_koogeek_p1eu_setup(hass):
|
||||
"""Test that a Koogeek P1EU can be correctly setup in HA."""
|
||||
accessories = await setup_accessories_from_file(hass, "koogeek_p1eu.json")
|
||||
config_entry, pairing = await setup_test_accessories(hass, accessories)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
# Check that the switch entity is handled correctly
|
||||
|
||||
entry = entity_registry.async_get("switch.koogeek_p1_a00aa0")
|
||||
assert entry.unique_id == "homekit-EUCP03190xxxxx48-7"
|
||||
|
||||
helper = Helper(
|
||||
hass, "switch.koogeek_p1_a00aa0", pairing, accessories[0], config_entry
|
||||
)
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["friendly_name"] == "Koogeek-P1-A00AA0"
|
||||
|
||||
device = device_registry.async_get(entry.device_id)
|
||||
assert device.manufacturer == "Koogeek"
|
||||
assert device.name == "Koogeek-P1-A00AA0"
|
||||
assert device.model == "P1EU"
|
||||
assert device.sw_version == "2.3.7"
|
||||
assert device.via_device_id is None
|
||||
|
||||
# Assert the power sensor is detected
|
||||
entry = entity_registry.async_get("sensor.koogeek_p1_a00aa0_real_time_energy")
|
||||
assert entry.unique_id == "homekit-EUCP03190xxxxx48-aid:1-sid:21-cid:21"
|
||||
|
||||
helper = Helper(
|
||||
hass,
|
||||
"sensor.koogeek_p1_a00aa0_real_time_energy",
|
||||
pairing,
|
||||
accessories[0],
|
||||
config_entry,
|
||||
)
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["friendly_name"] == "Koogeek-P1-A00AA0 - Real Time Energy"
|
||||
|
||||
# The sensor and switch should be part of the same device
|
||||
assert entry.device_id == device.id
|
@ -9,7 +9,7 @@ from homeassistant.const import (
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
)
|
||||
|
||||
from tests.components.homekit_controller.common import setup_test_component
|
||||
from tests.components.homekit_controller.common import Helper, setup_test_component
|
||||
|
||||
TEMPERATURE = ("temperature", "temperature.current")
|
||||
HUMIDITY = ("humidity", "relative-humidity.current")
|
||||
@ -18,6 +18,7 @@ CARBON_DIOXIDE_LEVEL = ("carbon-dioxide", "carbon-dioxide.level")
|
||||
BATTERY_LEVEL = ("battery", "battery-level")
|
||||
CHARGING_STATE = ("battery", "charging-state")
|
||||
LO_BATT = ("battery", "status-lo-batt")
|
||||
ON = ("outlet", "on")
|
||||
|
||||
|
||||
def create_temperature_sensor_service(accessory):
|
||||
@ -183,3 +184,45 @@ async def test_battery_low(hass, utcnow):
|
||||
helper.characteristics[LO_BATT].value = 1
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["icon"] == "mdi:battery-alert"
|
||||
|
||||
|
||||
def create_switch_with_sensor(accessory):
|
||||
"""Define battery level characteristics."""
|
||||
service = accessory.add_service(ServicesTypes.OUTLET)
|
||||
|
||||
realtime_energy = service.add_char(
|
||||
CharacteristicsTypes.Vendor.KOOGEEK_REALTIME_ENERGY
|
||||
)
|
||||
realtime_energy.value = 0
|
||||
realtime_energy.format = "float"
|
||||
|
||||
cur_state = service.add_char(CharacteristicsTypes.ON)
|
||||
cur_state.value = True
|
||||
|
||||
return service
|
||||
|
||||
|
||||
async def test_switch_with_sensor(hass, utcnow):
|
||||
"""Test a switch service that has a sensor characteristic is correctly handled."""
|
||||
helper = await setup_test_component(hass, create_switch_with_sensor)
|
||||
outlet = helper.accessory.services.first(service_type=ServicesTypes.OUTLET)
|
||||
|
||||
# Helper will be for the primary entity, which is the outlet. Make a helper for the sensor.
|
||||
energy_helper = Helper(
|
||||
hass,
|
||||
"sensor.testdevice_real_time_energy",
|
||||
helper.pairing,
|
||||
helper.accessory,
|
||||
helper.config_entry,
|
||||
)
|
||||
|
||||
outlet = energy_helper.accessory.services.first(service_type=ServicesTypes.OUTLET)
|
||||
realtime_energy = outlet[CharacteristicsTypes.Vendor.KOOGEEK_REALTIME_ENERGY]
|
||||
|
||||
realtime_energy.value = 1
|
||||
state = await energy_helper.poll_and_get_state()
|
||||
assert state.state == "1"
|
||||
|
||||
realtime_energy.value = 50
|
||||
state = await energy_helper.poll_and_get_state()
|
||||
assert state.state == "50"
|
||||
|
392
tests/fixtures/homekit_controller/koogeek_p1eu.json
vendored
Normal file
392
tests/fixtures/homekit_controller/koogeek_p1eu.json
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
[
|
||||
{
|
||||
"aid": 1,
|
||||
"services": [
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 2,
|
||||
"maxLen": 64,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "Koogeek-P1-A00AA0"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 3,
|
||||
"maxLen": 64,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000020-0000-1000-8000-0026BB765291",
|
||||
"value": "Koogeek"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 4,
|
||||
"maxLen": 64,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000021-0000-1000-8000-0026BB765291",
|
||||
"value": "P1EU"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 5,
|
||||
"maxLen": 64,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000030-0000-1000-8000-0026BB765291",
|
||||
"value": "EUCP03190xxxxx48"
|
||||
},
|
||||
{
|
||||
"format": "bool",
|
||||
"iid": 6,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "00000014-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 37,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000052-0000-1000-8000-0026BB765291",
|
||||
"value": "2.3.7"
|
||||
}
|
||||
],
|
||||
"iid": 1,
|
||||
"stype": "accessory-information",
|
||||
"type": "0000003E-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 8,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000025-0000-1000-8000-0026BB765291",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 9,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000026-0000-1000-8000-0026BB765291",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 10,
|
||||
"maxLen": 64,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "outlet"
|
||||
}
|
||||
],
|
||||
"iid": 7,
|
||||
"primary": true,
|
||||
"stype": "outlet",
|
||||
"type": "00000047-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "TIMER_SETTINGS",
|
||||
"format": "tlv8",
|
||||
"iid": 12,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw"
|
||||
],
|
||||
"type": "4AAAF942-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
}
|
||||
],
|
||||
"iid": 11,
|
||||
"stype": "Unknown Service: 4AAAF940-0DEC-11E5-B939-0800200C9A66",
|
||||
"type": "4AAAF940-0DEC-11E5-B939-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "FW Upgrade supported types",
|
||||
"format": "string",
|
||||
"iid": 14,
|
||||
"perms": [
|
||||
"pr",
|
||||
"hd"
|
||||
],
|
||||
"type": "151909D2-3802-11E4-916C-0800200C9A66",
|
||||
"value": "url,data"
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade URL",
|
||||
"format": "string",
|
||||
"iid": 15,
|
||||
"maxLen": 256,
|
||||
"perms": [
|
||||
"pw",
|
||||
"hd"
|
||||
],
|
||||
"type": "151909D1-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade Status",
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 16,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev",
|
||||
"hd"
|
||||
],
|
||||
"type": "151909D6-3802-11E4-916C-0800200C9A66",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade Data",
|
||||
"format": "data",
|
||||
"iid": 17,
|
||||
"perms": [
|
||||
"pw",
|
||||
"hd"
|
||||
],
|
||||
"type": "151909D7-3802-11E4-916C-0800200C9A66"
|
||||
}
|
||||
],
|
||||
"hidden": true,
|
||||
"iid": 13,
|
||||
"stype": "Unknown Service: 151909D0-3802-11E4-916C-0800200C9A66",
|
||||
"type": "151909D0-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "Timezone",
|
||||
"format": "int",
|
||||
"iid": 19,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw"
|
||||
],
|
||||
"type": "151909D5-3802-11E4-916C-0800200C9A66",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "Time value since Epoch",
|
||||
"format": "int",
|
||||
"iid": 20,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw"
|
||||
],
|
||||
"type": "151909D4-3802-11E4-916C-0800200C9A66",
|
||||
"value": 1570358601
|
||||
}
|
||||
],
|
||||
"iid": 18,
|
||||
"stype": "Unknown Service: 151909D3-3802-11E4-916C-0800200C9A66",
|
||||
"type": "151909D3-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "1 REALTIME_ENERGY",
|
||||
"ev": false,
|
||||
"format": "float",
|
||||
"iid": 22,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "4AAAF931-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"description": "2 CURRENT_HOUR_DATA",
|
||||
"ev": false,
|
||||
"format": "float",
|
||||
"iid": 23,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "4AAAF932-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "3 HOUR_DATA_TODAY",
|
||||
"format": "tlv8",
|
||||
"iid": 24,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF933-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "4 HOUR_DATA_YESTERDAY",
|
||||
"format": "tlv8",
|
||||
"iid": 25,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF934-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "5 HOUR_DATA_2_DAYS_BEFORE",
|
||||
"format": "tlv8",
|
||||
"iid": 26,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF935-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "6 HOUR_DATA_3_DAYS_BEFORE",
|
||||
"format": "tlv8",
|
||||
"iid": 27,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF936-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "7 HOUR_DATA_4_DAYS_BEFORE",
|
||||
"format": "tlv8",
|
||||
"iid": 28,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF937-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "8 HOUR_DATA_5_DAYS_BEFORE",
|
||||
"format": "tlv8",
|
||||
"iid": 29,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF938-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "9 HOUR_DATA_6_DAYS_BEFORE",
|
||||
"format": "tlv8",
|
||||
"iid": 30,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF939-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "10 HOUR_DATA_7_DAYS_BEFORE",
|
||||
"format": "tlv8",
|
||||
"iid": 31,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF93A-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "11 DAY_DATA_THIS_MONTH",
|
||||
"format": "tlv8",
|
||||
"iid": 32,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF93B-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
},
|
||||
{
|
||||
"description": "12 DAY_DATA_LAST_MONTH",
|
||||
"format": "tlv8",
|
||||
"iid": 33,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF93C-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "AHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
},
|
||||
{
|
||||
"description": "13 MONTH_DATA_THIS_YEAR",
|
||||
"format": "tlv8",
|
||||
"iid": 34,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF93D-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "ADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "14 MONTH_DATA_LAST_YEAR",
|
||||
"format": "tlv8",
|
||||
"iid": 35,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "4AAAF93E-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": "ADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
},
|
||||
{
|
||||
"description": "15 RUNNING_TIME",
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 36,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "4AAAF93F-0DEC-11E5-B939-0800200C9A66",
|
||||
"value": 0
|
||||
}
|
||||
],
|
||||
"iid": 21,
|
||||
"stype": "Unknown Service: 4AAAF930-0DEC-11E5-B939-0800200C9A66",
|
||||
"type": "4AAAF930-0DEC-11E5-B939-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 39,
|
||||
"maxLen": 64,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000037-0000-1000-8000-0026BB765291",
|
||||
"value": "1.1.0"
|
||||
}
|
||||
],
|
||||
"iid": 38,
|
||||
"stype": "service",
|
||||
"type": "000000A2-0000-1000-8000-0026BB765291"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user