Refactor SmartThings (#137940)

This commit is contained in:
Joost Lekkerkerker 2025-02-26 15:14:04 +01:00 committed by GitHub
parent bb9aba2a7d
commit bb120020a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
109 changed files with 22599 additions and 6175 deletions

2
CODEOWNERS generated
View File

@ -1401,6 +1401,8 @@ build.json @home-assistant/supervisor
/tests/components/smappee/ @bsmappee /tests/components/smappee/ @bsmappee
/homeassistant/components/smart_meter_texas/ @grahamwetzler /homeassistant/components/smart_meter_texas/ @grahamwetzler
/tests/components/smart_meter_texas/ @grahamwetzler /tests/components/smart_meter_texas/ @grahamwetzler
/homeassistant/components/smartthings/ @joostlek
/tests/components/smartthings/ @joostlek
/homeassistant/components/smarttub/ @mdz /homeassistant/components/smarttub/ @mdz
/tests/components/smarttub/ @mdz /tests/components/smarttub/ @mdz
/homeassistant/components/smarty/ @z0mbieprocess /homeassistant/components/smarty/ @z0mbieprocess

View File

@ -2,416 +2,144 @@
from __future__ import annotations from __future__ import annotations
import asyncio from dataclasses import dataclass
from collections.abc import Iterable
from http import HTTPStatus
import importlib
import logging import logging
from typing import TYPE_CHECKING
from aiohttp.client_exceptions import ClientConnectionError, ClientResponseError from aiohttp import ClientError
from pysmartapp.event import EVENT_TYPE_DEVICE from pysmartthings import (
from pysmartthings import APIInvalidGrant, Attribute, Capability, SmartThings Attribute,
Capability,
Device,
Scene,
SmartThings,
SmartThingsAuthenticationFailedError,
Status,
)
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ( from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
ConfigEntryAuthFailed,
ConfigEntryError,
ConfigEntryNotReady,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.config_entry_oauth2_flow import (
from homeassistant.helpers.event import async_track_time_interval OAuth2Session,
from homeassistant.helpers.typing import ConfigType async_get_config_entry_implementation,
from homeassistant.loader import async_get_loaded_integration )
from homeassistant.setup import SetupPhases, async_pause_setup
from .config_flow import SmartThingsFlowHandler # noqa: F401 from .const import CONF_INSTALLED_APP_ID, CONF_LOCATION_ID, MAIN, OLD_DATA
from .const import (
CONF_APP_ID,
CONF_INSTALLED_APP_ID,
CONF_LOCATION_ID,
CONF_REFRESH_TOKEN,
DATA_BROKERS,
DATA_MANAGER,
DOMAIN,
EVENT_BUTTON,
PLATFORMS,
SIGNAL_SMARTTHINGS_UPDATE,
TOKEN_REFRESH_INTERVAL,
)
from .smartapp import (
format_unique_id,
setup_smartapp,
setup_smartapp_endpoint,
smartapp_sync_subscriptions,
unload_smartapp_endpoint,
validate_installed_app,
validate_webhook_requirements,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
@dataclass
class SmartThingsData:
"""Define an object to hold SmartThings data."""
devices: dict[str, FullDevice]
scenes: dict[str, Scene]
client: SmartThings
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @dataclass
"""Initialize the SmartThings platform.""" class FullDevice:
await setup_smartapp_endpoint(hass, False) """Define an object to hold device data."""
return True
device: Device
status: dict[str, dict[Capability, dict[Attribute, Status]]]
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: type SmartThingsConfigEntry = ConfigEntry[SmartThingsData]
"""Handle migration of a previous version config entry.
A config entry created under a previous version must go through the PLATFORMS = [
integration setup again so we can properly retrieve the needed data Platform.BINARY_SENSOR,
elements. Force this by removing the entry and triggering a new flow. Platform.CLIMATE,
""" Platform.COVER,
# Remove the entry which will invoke the callback to delete the app. Platform.FAN,
hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) Platform.LIGHT,
# only create new flow if there isn't a pending one for SmartThings. Platform.LOCK,
if not hass.config_entries.flow.async_progress_by_handler(DOMAIN): Platform.SCENE,
hass.async_create_task( Platform.SENSOR,
hass.config_entries.flow.async_init( Platform.SWITCH,
DOMAIN, context={"source": SOURCE_IMPORT} ]
)
)
# Return False because it could not be migrated.
return False
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: SmartThingsConfigEntry) -> bool:
"""Initialize config entry which represents an installed SmartApp.""" """Initialize config entry which represents an installed SmartApp."""
# For backwards compat # The oauth smartthings entry will have a token, older ones are version 3
if entry.unique_id is None: # after migration but still require reauthentication
hass.config_entries.async_update_entry( if CONF_TOKEN not in entry.data:
entry, raise ConfigEntryAuthFailed("Config entry missing token")
unique_id=format_unique_id( implementation = await async_get_config_entry_implementation(hass, entry)
entry.data[CONF_APP_ID], entry.data[CONF_LOCATION_ID] session = OAuth2Session(hass, entry, implementation)
),
)
if not validate_webhook_requirements(hass):
_LOGGER.warning(
"The 'base_url' of the 'http' integration must be configured and start with"
" 'https://'"
)
return False
api = SmartThings(async_get_clientsession(hass), entry.data[CONF_ACCESS_TOKEN])
# Ensure platform modules are loaded since the DeviceBroker will
# import them below and we want them to be cached ahead of time
# so the integration does not do blocking I/O in the event loop
# to import the modules.
await async_get_loaded_integration(hass, DOMAIN).async_get_platforms(PLATFORMS)
try: try:
# See if the app is already setup. This occurs when there are await session.async_ensure_token_valid()
# installs in multiple SmartThings locations (valid use-case) except ClientError as err:
manager = hass.data[DOMAIN][DATA_MANAGER] raise ConfigEntryNotReady from err
smart_app = manager.smartapps.get(entry.data[CONF_APP_ID])
if not smart_app:
# Validate and setup the app.
app = await api.app(entry.data[CONF_APP_ID])
smart_app = setup_smartapp(hass, app)
# Validate and retrieve the installed app. client = SmartThings(session=async_get_clientsession(hass))
installed_app = await validate_installed_app(
api, entry.data[CONF_INSTALLED_APP_ID]
)
# Get scenes async def _refresh_token() -> str:
scenes = await async_get_entry_scenes(entry, api) await session.async_ensure_token_valid()
token = session.token[CONF_ACCESS_TOKEN]
if TYPE_CHECKING:
assert isinstance(token, str)
return token
# Get SmartApp token to sync subscriptions client.refresh_token_function = _refresh_token
token = await api.generate_tokens(
entry.data[CONF_CLIENT_ID],
entry.data[CONF_CLIENT_SECRET],
entry.data[CONF_REFRESH_TOKEN],
)
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_REFRESH_TOKEN: token.refresh_token}
)
# Get devices and their current status device_status: dict[str, FullDevice] = {}
devices = await api.devices(location_ids=[installed_app.location_id]) try:
devices = await client.get_devices()
for device in devices:
status = await client.get_device_status(device.device_id)
device_status[device.device_id] = FullDevice(device=device, status=status)
except SmartThingsAuthenticationFailedError as err:
raise ConfigEntryAuthFailed from err
async def retrieve_device_status(device): scenes = {
try: scene.scene_id: scene
await device.status.refresh() for scene in await client.get_scenes(location_id=entry.data[CONF_LOCATION_ID])
except ClientResponseError: }
_LOGGER.debug(
(
"Unable to update status for device: %s (%s), the device will"
" be excluded"
),
device.label,
device.device_id,
exc_info=True,
)
devices.remove(device)
await asyncio.gather(*(retrieve_device_status(d) for d in devices.copy())) entry.runtime_data = SmartThingsData(
devices={
device_id: device
for device_id, device in device_status.items()
if MAIN in device.status
},
client=client,
scenes=scenes,
)
# Sync device subscriptions entry.async_create_background_task(
await smartapp_sync_subscriptions( hass,
hass, client.subscribe(
token.access_token, entry.data[CONF_LOCATION_ID], entry.data[CONF_TOKEN][CONF_INSTALLED_APP_ID]
installed_app.location_id, ),
installed_app.installed_app_id, "smartthings_webhook",
devices, )
)
# Setup device broker
with async_pause_setup(hass, SetupPhases.WAIT_IMPORT_PLATFORMS):
# DeviceBroker has a side effect of importing platform
# modules when its created. In the future this should be
# refactored to not do this.
broker = await hass.async_add_import_executor_job(
DeviceBroker, hass, entry, token, smart_app, devices, scenes
)
broker.connect()
hass.data[DOMAIN][DATA_BROKERS][entry.entry_id] = broker
except APIInvalidGrant as ex:
raise ConfigEntryAuthFailed from ex
except ClientResponseError as ex:
if ex.status in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
raise ConfigEntryError(
"The access token is no longer valid. Please remove the integration and set up again."
) from ex
_LOGGER.debug(ex, exc_info=True)
raise ConfigEntryNotReady from ex
except (ClientConnectionError, RuntimeWarning) as ex:
_LOGGER.debug(ex, exc_info=True)
raise ConfigEntryNotReady from ex
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True
async def async_get_entry_scenes(entry: ConfigEntry, api): async def async_unload_entry(
"""Get the scenes within an integration.""" hass: HomeAssistant, entry: SmartThingsConfigEntry
try: ) -> bool:
return await api.scenes(location_id=entry.data[CONF_LOCATION_ID])
except ClientResponseError as ex:
if ex.status == HTTPStatus.FORBIDDEN:
_LOGGER.exception(
(
"Unable to load scenes for configuration entry '%s' because the"
" access token does not have the required access"
),
entry.title,
)
else:
raise
return []
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS].pop(entry.entry_id, None)
if broker:
broker.disconnect()
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Perform clean-up when entry is being removed.""" """Handle config entry migration."""
api = SmartThings(async_get_clientsession(hass), entry.data[CONF_ACCESS_TOKEN])
# Remove the installed_app, which if already removed raises a HTTPStatus.FORBIDDEN error. if entry.version < 3:
installed_app_id = entry.data[CONF_INSTALLED_APP_ID] # We keep the old data around, so we can use that to clean up the webhook in the future
try: hass.config_entries.async_update_entry(
await api.delete_installed_app(installed_app_id) entry, version=3, data={OLD_DATA: dict(entry.data)}
except ClientResponseError as ex:
if ex.status == HTTPStatus.FORBIDDEN:
_LOGGER.debug(
"Installed app %s has already been removed",
installed_app_id,
exc_info=True,
)
else:
raise
_LOGGER.debug("Removed installed app %s", installed_app_id)
# Remove the app if not referenced by other entries, which if already
# removed raises a HTTPStatus.FORBIDDEN error.
all_entries = hass.config_entries.async_entries(DOMAIN)
app_id = entry.data[CONF_APP_ID]
app_count = sum(1 for entry in all_entries if entry.data[CONF_APP_ID] == app_id)
if app_count > 1:
_LOGGER.debug(
(
"App %s was not removed because it is in use by other configuration"
" entries"
),
app_id,
)
return
# Remove the app
try:
await api.delete_app(app_id)
except ClientResponseError as ex:
if ex.status == HTTPStatus.FORBIDDEN:
_LOGGER.debug("App %s has already been removed", app_id, exc_info=True)
else:
raise
_LOGGER.debug("Removed app %s", app_id)
if len(all_entries) == 1:
await unload_smartapp_endpoint(hass)
class DeviceBroker:
"""Manages an individual SmartThings config entry."""
def __init__(
self,
hass: HomeAssistant,
entry: ConfigEntry,
token,
smart_app,
devices: Iterable,
scenes: Iterable,
) -> None:
"""Create a new instance of the DeviceBroker."""
self._hass = hass
self._entry = entry
self._installed_app_id = entry.data[CONF_INSTALLED_APP_ID]
self._smart_app = smart_app
self._token = token
self._event_disconnect = None
self._regenerate_token_remove = None
self._assignments = self._assign_capabilities(devices)
self.devices = {device.device_id: device for device in devices}
self.scenes = {scene.scene_id: scene for scene in scenes}
def _assign_capabilities(self, devices: Iterable):
"""Assign platforms to capabilities."""
assignments = {}
for device in devices:
capabilities = device.capabilities.copy()
slots = {}
for platform in PLATFORMS:
platform_module = importlib.import_module(
f".{platform}", self.__module__
)
if not hasattr(platform_module, "get_capabilities"):
continue
assigned = platform_module.get_capabilities(capabilities)
if not assigned:
continue
# Draw-down capabilities and set slot assignment
for capability in assigned:
if capability not in capabilities:
continue
capabilities.remove(capability)
slots[capability] = platform
assignments[device.device_id] = slots
return assignments
def connect(self):
"""Connect handlers/listeners for device/lifecycle events."""
# Setup interval to regenerate the refresh token on a periodic basis.
# Tokens expire in 30 days and once expired, cannot be recovered.
async def regenerate_refresh_token(now):
"""Generate a new refresh token and update the config entry."""
await self._token.refresh(
self._entry.data[CONF_CLIENT_ID],
self._entry.data[CONF_CLIENT_SECRET],
)
self._hass.config_entries.async_update_entry(
self._entry,
data={
**self._entry.data,
CONF_REFRESH_TOKEN: self._token.refresh_token,
},
)
_LOGGER.debug(
"Regenerated refresh token for installed app: %s",
self._installed_app_id,
)
self._regenerate_token_remove = async_track_time_interval(
self._hass, regenerate_refresh_token, TOKEN_REFRESH_INTERVAL
) )
# Connect handler to incoming device events return True
self._event_disconnect = self._smart_app.connect_event(self._event_handler)
def disconnect(self):
"""Disconnects handlers/listeners for device/lifecycle events."""
if self._regenerate_token_remove:
self._regenerate_token_remove()
if self._event_disconnect:
self._event_disconnect()
def get_assigned(self, device_id: str, platform: str):
"""Get the capabilities assigned to the platform."""
slots = self._assignments.get(device_id, {})
return [key for key, value in slots.items() if value == platform]
def any_assigned(self, device_id: str, platform: str):
"""Return True if the platform has any assigned capabilities."""
slots = self._assignments.get(device_id, {})
return any(value for value in slots.values() if value == platform)
async def _event_handler(self, req, resp, app):
"""Broker for incoming events."""
# Do not process events received from a different installed app
# under the same parent SmartApp (valid use-scenario)
if req.installed_app_id != self._installed_app_id:
return
updated_devices = set()
for evt in req.events:
if evt.event_type != EVENT_TYPE_DEVICE:
continue
if not (device := self.devices.get(evt.device_id)):
continue
device.status.apply_attribute_update(
evt.component_id,
evt.capability,
evt.attribute,
evt.value,
data=evt.data,
)
# Fire events for buttons
if (
evt.capability == Capability.button
and evt.attribute == Attribute.button
):
data = {
"component_id": evt.component_id,
"device_id": evt.device_id,
"location_id": evt.location_id,
"value": evt.value,
"name": device.label,
"data": evt.data,
}
self._hass.bus.async_fire(EVENT_BUTTON, data)
_LOGGER.debug("Fired button event: %s", data)
else:
data = {
"location_id": evt.location_id,
"device_id": evt.device_id,
"component_id": evt.component_id,
"capability": evt.capability,
"attribute": evt.attribute,
"value": evt.value,
"data": evt.data,
}
_LOGGER.debug("Push update received: %s", data)
updated_devices.add(device.device_id)
async_dispatcher_send(self._hass, SIGNAL_SMARTTHINGS_UPDATE, updated_devices)

View File

@ -0,0 +1,64 @@
"""Application credentials platform for SmartThings."""
from json import JSONDecodeError
import logging
from typing import cast
from aiohttp import BasicAuth, ClientError
from homeassistant.components.application_credentials import (
AuthImplementation,
AuthorizationServer,
ClientCredential,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2Implementation
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_get_auth_implementation(
hass: HomeAssistant, auth_domain: str, credential: ClientCredential
) -> AbstractOAuth2Implementation:
"""Return auth implementation."""
return SmartThingsOAuth2Implementation(
hass,
DOMAIN,
credential,
authorization_server=AuthorizationServer(
authorize_url="https://api.smartthings.com/oauth/authorize",
token_url="https://auth-global.api.smartthings.com/oauth/token",
),
)
class SmartThingsOAuth2Implementation(AuthImplementation):
"""Oauth2 implementation that only uses the external url."""
async def _token_request(self, data: dict) -> dict:
"""Make a token request."""
session = async_get_clientsession(self.hass)
resp = await session.post(
self.token_url,
data=data,
auth=BasicAuth(self.client_id, self.client_secret),
)
if resp.status >= 400:
try:
error_response = await resp.json()
except (ClientError, JSONDecodeError):
error_response = {}
error_code = error_response.get("error", "unknown")
error_description = error_response.get("error_description", "unknown error")
_LOGGER.error(
"Token request for %s failed (%s): %s",
self.domain,
error_code,
error_description,
)
resp.raise_for_status()
return cast(dict, await resp.json())

View File

@ -2,84 +2,144 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence from dataclasses import dataclass
from pysmartthings import Attribute, Capability from pysmartthings import Attribute, Capability, SmartThings
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
CAPABILITY_TO_ATTRIB = {
Capability.acceleration_sensor: Attribute.acceleration, @dataclass(frozen=True, kw_only=True)
Capability.contact_sensor: Attribute.contact, class SmartThingsBinarySensorEntityDescription(BinarySensorEntityDescription):
Capability.filter_status: Attribute.filter_status, """Describe a SmartThings binary sensor entity."""
Capability.motion_sensor: Attribute.motion,
Capability.presence_sensor: Attribute.presence, is_on_key: str
Capability.sound_sensor: Attribute.sound,
Capability.tamper_alert: Attribute.tamper,
Capability.valve: Attribute.valve, CAPABILITY_TO_SENSORS: dict[
Capability.water_sensor: Attribute.water, Capability, dict[Attribute, SmartThingsBinarySensorEntityDescription]
} ] = {
ATTRIB_TO_CLASS = { Capability.ACCELERATION_SENSOR: {
Attribute.acceleration: BinarySensorDeviceClass.MOVING, Attribute.ACCELERATION: SmartThingsBinarySensorEntityDescription(
Attribute.contact: BinarySensorDeviceClass.OPENING, key=Attribute.ACCELERATION,
Attribute.filter_status: BinarySensorDeviceClass.PROBLEM, device_class=BinarySensorDeviceClass.MOVING,
Attribute.motion: BinarySensorDeviceClass.MOTION, is_on_key="active",
Attribute.presence: BinarySensorDeviceClass.PRESENCE, )
Attribute.sound: BinarySensorDeviceClass.SOUND, },
Attribute.tamper: BinarySensorDeviceClass.PROBLEM, Capability.CONTACT_SENSOR: {
Attribute.valve: BinarySensorDeviceClass.OPENING, Attribute.CONTACT: SmartThingsBinarySensorEntityDescription(
Attribute.water: BinarySensorDeviceClass.MOISTURE, key=Attribute.CONTACT,
} device_class=BinarySensorDeviceClass.DOOR,
ATTRIB_TO_ENTTIY_CATEGORY = { is_on_key="open",
Attribute.tamper: EntityCategory.DIAGNOSTIC, )
},
Capability.FILTER_STATUS: {
Attribute.FILTER_STATUS: SmartThingsBinarySensorEntityDescription(
key=Attribute.FILTER_STATUS,
device_class=BinarySensorDeviceClass.PROBLEM,
is_on_key="replace",
)
},
Capability.MOTION_SENSOR: {
Attribute.MOTION: SmartThingsBinarySensorEntityDescription(
key=Attribute.MOTION,
device_class=BinarySensorDeviceClass.MOTION,
is_on_key="active",
)
},
Capability.PRESENCE_SENSOR: {
Attribute.PRESENCE: SmartThingsBinarySensorEntityDescription(
key=Attribute.PRESENCE,
device_class=BinarySensorDeviceClass.PRESENCE,
is_on_key="present",
)
},
Capability.SOUND_SENSOR: {
Attribute.SOUND: SmartThingsBinarySensorEntityDescription(
key=Attribute.SOUND,
device_class=BinarySensorDeviceClass.SOUND,
is_on_key="detected",
)
},
Capability.TAMPER_ALERT: {
Attribute.TAMPER: SmartThingsBinarySensorEntityDescription(
key=Attribute.TAMPER,
device_class=BinarySensorDeviceClass.PROBLEM,
is_on_key="detected",
entity_category=EntityCategory.DIAGNOSTIC,
)
},
Capability.VALVE: {
Attribute.VALVE: SmartThingsBinarySensorEntityDescription(
key=Attribute.VALVE,
device_class=BinarySensorDeviceClass.OPENING,
is_on_key="open",
)
},
Capability.WATER_SENSOR: {
Attribute.WATER: SmartThingsBinarySensorEntityDescription(
key=Attribute.WATER,
device_class=BinarySensorDeviceClass.MOISTURE,
is_on_key="wet",
)
},
} }
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add binary sensors for a config entry.""" """Add binary sensors for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] entry_data = entry.runtime_data
sensors = [] async_add_entities(
for device in broker.devices.values(): SmartThingsBinarySensor(
for capability in broker.get_assigned(device.device_id, "binary_sensor"): entry_data.client, device, description, capability, attribute
attrib = CAPABILITY_TO_ATTRIB[capability] )
sensors.append(SmartThingsBinarySensor(device, attrib)) for device in entry_data.devices.values()
async_add_entities(sensors) for capability, attribute_map in CAPABILITY_TO_SENSORS.items()
if capability in device.status[MAIN]
for attribute, description in attribute_map.items()
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None: )
"""Return all capabilities supported if minimum required are present."""
return [
capability for capability in CAPABILITY_TO_ATTRIB if capability in capabilities
]
class SmartThingsBinarySensor(SmartThingsEntity, BinarySensorEntity): class SmartThingsBinarySensor(SmartThingsEntity, BinarySensorEntity):
"""Define a SmartThings Binary Sensor.""" """Define a SmartThings Binary Sensor."""
def __init__(self, device, attribute): entity_description: SmartThingsBinarySensorEntityDescription
def __init__(
self,
client: SmartThings,
device: FullDevice,
entity_description: SmartThingsBinarySensorEntityDescription,
capability: Capability,
attribute: Attribute,
) -> None:
"""Init the class.""" """Init the class."""
super().__init__(device) super().__init__(client, device, {capability})
self._attribute = attribute self._attribute = attribute
self._attr_name = f"{device.label} {attribute}" self.capability = capability
self._attr_unique_id = f"{device.device_id}.{attribute}" self.entity_description = entity_description
self._attr_device_class = ATTRIB_TO_CLASS[attribute] self._attr_name = f"{device.device.label} {attribute}"
self._attr_entity_category = ATTRIB_TO_ENTTIY_CATEGORY.get(attribute) self._attr_unique_id = f"{device.device.device_id}.{attribute}"
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return true if the binary sensor is on.""" """Return true if the binary sensor is on."""
return self._device.status.is_on(self._attribute) return (
self.get_attribute_value(self.capability, self._attribute)
== self.entity_description.is_on_key
)

View File

@ -3,17 +3,15 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Iterable, Sequence
import logging import logging
from typing import Any from typing import Any
from pysmartthings import Attribute, Capability from pysmartthings import Attribute, Capability, Command, SmartThings
from homeassistant.components.climate import ( from homeassistant.components.climate import (
ATTR_HVAC_MODE, ATTR_HVAC_MODE,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_LOW,
DOMAIN as CLIMATE_DOMAIN,
SWING_BOTH, SWING_BOTH,
SWING_HORIZONTAL, SWING_HORIZONTAL,
SWING_OFF, SWING_OFF,
@ -23,12 +21,12 @@ from homeassistant.components.climate import (
HVACAction, HVACAction,
HVACMode, HVACMode,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
ATTR_OPERATION_STATE = "operation_state" ATTR_OPERATION_STATE = "operation_state"
@ -97,124 +95,106 @@ UNIT_MAP = {"C": UnitOfTemperature.CELSIUS, "F": UnitOfTemperature.FAHRENHEIT}
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
AC_CAPABILITIES = [
Capability.AIR_CONDITIONER_MODE,
Capability.AIR_CONDITIONER_FAN_MODE,
Capability.SWITCH,
Capability.TEMPERATURE_MEASUREMENT,
Capability.THERMOSTAT_COOLING_SETPOINT,
]
THERMOSTAT_CAPABILITIES = [
Capability.TEMPERATURE_MEASUREMENT,
Capability.THERMOSTAT_HEATING_SETPOINT,
Capability.THERMOSTAT_MODE,
]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add climate entities for a config entry.""" """Add climate entities for a config entry."""
ac_capabilities = [ entry_data = entry.runtime_data
Capability.air_conditioner_mode, entities: list[ClimateEntity] = [
Capability.air_conditioner_fan_mode, SmartThingsAirConditioner(entry_data.client, device)
Capability.switch, for device in entry_data.devices.values()
Capability.temperature_measurement, if all(capability in device.status[MAIN] for capability in AC_CAPABILITIES)
Capability.thermostat_cooling_setpoint,
] ]
entities.extend(
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] SmartThingsThermostat(entry_data.client, device)
entities: list[ClimateEntity] = [] for device in entry_data.devices.values()
for device in broker.devices.values(): if all(
if not broker.any_assigned(device.device_id, CLIMATE_DOMAIN): capability in device.status[MAIN] for capability in THERMOSTAT_CAPABILITIES
continue )
if all(capability in device.capabilities for capability in ac_capabilities): )
entities.append(SmartThingsAirConditioner(device)) async_add_entities(entities)
else:
entities.append(SmartThingsThermostat(device))
async_add_entities(entities, True)
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
"""Return all capabilities supported if minimum required are present."""
supported = [
Capability.air_conditioner_mode,
Capability.demand_response_load_control,
Capability.air_conditioner_fan_mode,
Capability.switch,
Capability.thermostat,
Capability.thermostat_cooling_setpoint,
Capability.thermostat_fan_mode,
Capability.thermostat_heating_setpoint,
Capability.thermostat_mode,
Capability.thermostat_operating_state,
]
# Can have this legacy/deprecated capability
if Capability.thermostat in capabilities:
return supported
# Or must have all of these thermostat capabilities
thermostat_capabilities = [
Capability.temperature_measurement,
Capability.thermostat_heating_setpoint,
Capability.thermostat_mode,
]
if all(capability in capabilities for capability in thermostat_capabilities):
return supported
# Or must have all of these A/C capabilities
ac_capabilities = [
Capability.air_conditioner_mode,
Capability.air_conditioner_fan_mode,
Capability.switch,
Capability.temperature_measurement,
Capability.thermostat_cooling_setpoint,
]
if all(capability in capabilities for capability in ac_capabilities):
return supported
return None
class SmartThingsThermostat(SmartThingsEntity, ClimateEntity): class SmartThingsThermostat(SmartThingsEntity, ClimateEntity):
"""Define a SmartThings climate entities.""" """Define a SmartThings climate entities."""
def __init__(self, device): def __init__(self, client: SmartThings, device: FullDevice) -> None:
"""Init the class.""" """Init the class."""
super().__init__(device) super().__init__(
client,
device,
{
Capability.THERMOSTAT_FAN_MODE,
Capability.THERMOSTAT_MODE,
Capability.TEMPERATURE_MEASUREMENT,
Capability.THERMOSTAT_HEATING_SETPOINT,
Capability.THERMOSTAT_OPERATING_STATE,
Capability.THERMOSTAT_COOLING_SETPOINT,
Capability.RELATIVE_HUMIDITY_MEASUREMENT,
},
)
self._attr_supported_features = self._determine_features() self._attr_supported_features = self._determine_features()
self._hvac_mode = None
self._hvac_modes = None
def _determine_features(self): def _determine_features(self) -> ClimateEntityFeature:
flags = ( flags = (
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_ON
) )
if self._device.get_capability( if self.get_attribute_value(
Capability.thermostat_fan_mode, Capability.thermostat Capability.THERMOSTAT_FAN_MODE, Attribute.THERMOSTAT_FAN_MODE
): ):
flags |= ClimateEntityFeature.FAN_MODE flags |= ClimateEntityFeature.FAN_MODE
return flags return flags
async def async_set_fan_mode(self, fan_mode: str) -> None: async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new target fan mode.""" """Set new target fan mode."""
await self._device.set_thermostat_fan_mode(fan_mode, set_status=True) await self.execute_device_command(
Capability.THERMOSTAT_FAN_MODE,
# State is set optimistically in the command above, therefore update Command.SET_THERMOSTAT_FAN_MODE,
# the entity state ahead of receiving the confirming push updates argument=fan_mode,
self.async_schedule_update_ha_state(True) )
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target operation mode.""" """Set new target operation mode."""
mode = STATE_TO_MODE[hvac_mode] await self.execute_device_command(
await self._device.set_thermostat_mode(mode, set_status=True) Capability.THERMOSTAT_MODE,
Command.SET_THERMOSTAT_MODE,
# State is set optimistically in the command above, therefore update argument=STATE_TO_MODE[hvac_mode],
# the entity state ahead of receiving the confirming push updates )
self.async_schedule_update_ha_state(True)
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new operation mode and target temperatures.""" """Set new operation mode and target temperatures."""
hvac_mode = self.hvac_mode
# Operation state # Operation state
if operation_state := kwargs.get(ATTR_HVAC_MODE): if operation_state := kwargs.get(ATTR_HVAC_MODE):
mode = STATE_TO_MODE[operation_state] await self.async_set_hvac_mode(operation_state)
await self._device.set_thermostat_mode(mode, set_status=True) hvac_mode = operation_state
await self.async_update()
# Heat/cool setpoint # Heat/cool setpoint
heating_setpoint = None heating_setpoint = None
cooling_setpoint = None cooling_setpoint = None
if self.hvac_mode == HVACMode.HEAT: if hvac_mode == HVACMode.HEAT:
heating_setpoint = kwargs.get(ATTR_TEMPERATURE) heating_setpoint = kwargs.get(ATTR_TEMPERATURE)
elif self.hvac_mode == HVACMode.COOL: elif hvac_mode == HVACMode.COOL:
cooling_setpoint = kwargs.get(ATTR_TEMPERATURE) cooling_setpoint = kwargs.get(ATTR_TEMPERATURE)
else: else:
heating_setpoint = kwargs.get(ATTR_TARGET_TEMP_LOW) heating_setpoint = kwargs.get(ATTR_TARGET_TEMP_LOW)
@ -222,135 +202,145 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateEntity):
tasks = [] tasks = []
if heating_setpoint is not None: if heating_setpoint is not None:
tasks.append( tasks.append(
self._device.set_heating_setpoint( self.execute_device_command(
round(heating_setpoint, 3), set_status=True Capability.THERMOSTAT_HEATING_SETPOINT,
Command.SET_HEATING_SETPOINT,
argument=round(heating_setpoint, 3),
) )
) )
if cooling_setpoint is not None: if cooling_setpoint is not None:
tasks.append( tasks.append(
self._device.set_cooling_setpoint( self.execute_device_command(
round(cooling_setpoint, 3), set_status=True Capability.THERMOSTAT_COOLING_SETPOINT,
Command.SET_COOLING_SETPOINT,
argument=round(cooling_setpoint, 3),
) )
) )
await asyncio.gather(*tasks) await asyncio.gather(*tasks)
# State is set optimistically in the commands above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_schedule_update_ha_state(True)
async def async_update(self) -> None:
"""Update the attributes of the climate device."""
thermostat_mode = self._device.status.thermostat_mode
self._hvac_mode = MODE_TO_STATE.get(thermostat_mode)
if self._hvac_mode is None:
_LOGGER.debug(
"Device %s (%s) returned an invalid hvac mode: %s",
self._device.label,
self._device.device_id,
thermostat_mode,
)
modes = set()
supported_modes = self._device.status.supported_thermostat_modes
if isinstance(supported_modes, Iterable):
for mode in supported_modes:
if (state := MODE_TO_STATE.get(mode)) is not None:
modes.add(state)
else:
_LOGGER.debug(
(
"Device %s (%s) returned an invalid supported thermostat"
" mode: %s"
),
self._device.label,
self._device.device_id,
mode,
)
else:
_LOGGER.debug(
"Device %s (%s) returned invalid supported thermostat modes: %s",
self._device.label,
self._device.device_id,
supported_modes,
)
self._hvac_modes = list(modes)
@property @property
def current_humidity(self): def current_humidity(self) -> float | None:
"""Return the current humidity.""" """Return the current humidity."""
return self._device.status.humidity if self.supports_capability(Capability.RELATIVE_HUMIDITY_MEASUREMENT):
return self.get_attribute_value(
Capability.RELATIVE_HUMIDITY_MEASUREMENT, Attribute.HUMIDITY
)
return None
@property @property
def current_temperature(self): def current_temperature(self) -> float | None:
"""Return the current temperature.""" """Return the current temperature."""
return self._device.status.temperature return self.get_attribute_value(
Capability.TEMPERATURE_MEASUREMENT, Attribute.TEMPERATURE
)
@property @property
def fan_mode(self): def fan_mode(self) -> str | None:
"""Return the fan setting.""" """Return the fan setting."""
return self._device.status.thermostat_fan_mode return self.get_attribute_value(
Capability.THERMOSTAT_FAN_MODE, Attribute.THERMOSTAT_FAN_MODE
)
@property @property
def fan_modes(self): def fan_modes(self) -> list[str]:
"""Return the list of available fan modes.""" """Return the list of available fan modes."""
return self._device.status.supported_thermostat_fan_modes return self.get_attribute_value(
Capability.THERMOSTAT_FAN_MODE, Attribute.SUPPORTED_THERMOSTAT_FAN_MODES
)
@property @property
def hvac_action(self) -> HVACAction | None: def hvac_action(self) -> HVACAction | None:
"""Return the current running hvac operation if supported.""" """Return the current running hvac operation if supported."""
return OPERATING_STATE_TO_ACTION.get( return OPERATING_STATE_TO_ACTION.get(
self._device.status.thermostat_operating_state self.get_attribute_value(
Capability.THERMOSTAT_OPERATING_STATE,
Attribute.THERMOSTAT_OPERATING_STATE,
)
) )
@property @property
def hvac_mode(self) -> HVACMode: def hvac_mode(self) -> HVACMode | None:
"""Return current operation ie. heat, cool, idle.""" """Return current operation ie. heat, cool, idle."""
return self._hvac_mode return MODE_TO_STATE.get(
self.get_attribute_value(
Capability.THERMOSTAT_MODE, Attribute.THERMOSTAT_MODE
)
)
@property @property
def hvac_modes(self) -> list[HVACMode]: def hvac_modes(self) -> list[HVACMode]:
"""Return the list of available operation modes.""" """Return the list of available operation modes."""
return self._hvac_modes return [
state
for mode in self.get_attribute_value(
Capability.THERMOSTAT_MODE, Attribute.SUPPORTED_THERMOSTAT_MODES
)
if (state := AC_MODE_TO_STATE.get(mode)) is not None
]
@property @property
def target_temperature(self): def target_temperature(self) -> float | None:
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
if self.hvac_mode == HVACMode.COOL: if self.hvac_mode == HVACMode.COOL:
return self._device.status.cooling_setpoint return self.get_attribute_value(
Capability.THERMOSTAT_COOLING_SETPOINT, Attribute.COOLING_SETPOINT
)
if self.hvac_mode == HVACMode.HEAT: if self.hvac_mode == HVACMode.HEAT:
return self._device.status.heating_setpoint return self.get_attribute_value(
Capability.THERMOSTAT_HEATING_SETPOINT, Attribute.HEATING_SETPOINT
)
return None return None
@property @property
def target_temperature_high(self): def target_temperature_high(self) -> float | None:
"""Return the highbound target temperature we try to reach.""" """Return the highbound target temperature we try to reach."""
if self.hvac_mode == HVACMode.HEAT_COOL: if self.hvac_mode == HVACMode.HEAT_COOL:
return self._device.status.cooling_setpoint return self.get_attribute_value(
Capability.THERMOSTAT_COOLING_SETPOINT, Attribute.COOLING_SETPOINT
)
return None return None
@property @property
def target_temperature_low(self): def target_temperature_low(self):
"""Return the lowbound target temperature we try to reach.""" """Return the lowbound target temperature we try to reach."""
if self.hvac_mode == HVACMode.HEAT_COOL: if self.hvac_mode == HVACMode.HEAT_COOL:
return self._device.status.heating_setpoint return self.get_attribute_value(
Capability.THERMOSTAT_HEATING_SETPOINT, Attribute.HEATING_SETPOINT
)
return None return None
@property @property
def temperature_unit(self): def temperature_unit(self) -> str:
"""Return the unit of measurement.""" """Return the unit of measurement."""
return UNIT_MAP.get(self._device.status.attributes[Attribute.temperature].unit) unit = self._internal_state[Capability.TEMPERATURE_MEASUREMENT][
Attribute.TEMPERATURE
].unit
assert unit
return UNIT_MAP[unit]
class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity): class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
"""Define a SmartThings Air Conditioner.""" """Define a SmartThings Air Conditioner."""
_hvac_modes: list[HVACMode] _attr_preset_mode = None
def __init__(self, device) -> None: def __init__(self, client: SmartThings, device: FullDevice) -> None:
"""Init the class.""" """Init the class."""
super().__init__(device) super().__init__(
self._hvac_modes = [] client,
self._attr_preset_mode = None device,
{
Capability.AIR_CONDITIONER_MODE,
Capability.SWITCH,
Capability.FAN_OSCILLATION_MODE,
Capability.AIR_CONDITIONER_FAN_MODE,
Capability.THERMOSTAT_COOLING_SETPOINT,
Capability.TEMPERATURE_MEASUREMENT,
Capability.CUSTOM_AIR_CONDITIONER_OPTIONAL_MODE,
Capability.DEMAND_RESPONSE_LOAD_CONTROL,
},
)
self._attr_hvac_modes = self._determine_hvac_modes()
self._attr_preset_modes = self._determine_preset_modes() self._attr_preset_modes = self._determine_preset_modes()
self._attr_swing_modes = self._determine_swing_modes() self._attr_swing_modes = self._determine_swing_modes()
self._attr_supported_features = self._determine_supported_features() self._attr_supported_features = self._determine_supported_features()
@ -362,7 +352,7 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
| ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_ON
) )
if self._device.get_capability(Capability.fan_oscillation_mode): if self.supports_capability(Capability.FAN_OSCILLATION_MODE):
features |= ClimateEntityFeature.SWING_MODE features |= ClimateEntityFeature.SWING_MODE
if (self._attr_preset_modes is not None) and len(self._attr_preset_modes) > 0: if (self._attr_preset_modes is not None) and len(self._attr_preset_modes) > 0:
features |= ClimateEntityFeature.PRESET_MODE features |= ClimateEntityFeature.PRESET_MODE
@ -370,14 +360,11 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
async def async_set_fan_mode(self, fan_mode: str) -> None: async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new target fan mode.""" """Set new target fan mode."""
await self._device.set_fan_mode(fan_mode, set_status=True) await self.execute_device_command(
Capability.AIR_CONDITIONER_FAN_MODE,
# setting the fan must reset the preset mode (it deactivates the windFree function) Command.SET_FAN_MODE,
self._attr_preset_mode = None argument=fan_mode,
)
# State is set optimistically in the command above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_write_ha_state()
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target operation mode.""" """Set new target operation mode."""
@ -386,23 +373,27 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
return return
tasks = [] tasks = []
# Turn on the device if it's off before setting mode. # Turn on the device if it's off before setting mode.
if not self._device.status.switch: if self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "off":
tasks.append(self._device.switch_on(set_status=True)) tasks.append(self.async_turn_on())
mode = STATE_TO_AC_MODE[hvac_mode] mode = STATE_TO_AC_MODE[hvac_mode]
# If new hvac_mode is HVAC_MODE_FAN_ONLY and AirConditioner support "wind" mode the AirConditioner new mode has to be "wind" # If new hvac_mode is HVAC_MODE_FAN_ONLY and AirConditioner support "wind" mode the AirConditioner new mode has to be "wind"
# The conversion make the mode change working # The conversion make the mode change working
# The conversion is made only for device that wrongly has capability "wind" instead "fan_only" # The conversion is made only for device that wrongly has capability "wind" instead "fan_only"
if hvac_mode == HVACMode.FAN_ONLY: if hvac_mode == HVACMode.FAN_ONLY:
supported_modes = self._device.status.supported_ac_modes if WIND in self.get_attribute_value(
if WIND in supported_modes: Capability.AIR_CONDITIONER_MODE, Attribute.SUPPORTED_AC_MODES
):
mode = WIND mode = WIND
tasks.append(self._device.set_air_conditioner_mode(mode, set_status=True)) tasks.append(
self.execute_device_command(
Capability.AIR_CONDITIONER_MODE,
Command.SET_AIR_CONDITIONER_MODE,
argument=mode,
)
)
await asyncio.gather(*tasks) await asyncio.gather(*tasks)
# State is set optimistically in the command above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_write_ha_state()
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature.""" """Set new target temperature."""
@ -410,53 +401,44 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
# operation mode # operation mode
if operation_mode := kwargs.get(ATTR_HVAC_MODE): if operation_mode := kwargs.get(ATTR_HVAC_MODE):
if operation_mode == HVACMode.OFF: if operation_mode == HVACMode.OFF:
tasks.append(self._device.switch_off(set_status=True)) tasks.append(self.async_turn_off())
else: else:
if not self._device.status.switch: if (
tasks.append(self._device.switch_on(set_status=True)) self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH)
== "off"
):
tasks.append(self.async_turn_on())
tasks.append(self.async_set_hvac_mode(operation_mode)) tasks.append(self.async_set_hvac_mode(operation_mode))
# temperature # temperature
tasks.append( tasks.append(
self._device.set_cooling_setpoint(kwargs[ATTR_TEMPERATURE], set_status=True) self.execute_device_command(
Capability.THERMOSTAT_COOLING_SETPOINT,
Command.SET_COOLING_SETPOINT,
argument=kwargs[ATTR_TEMPERATURE],
)
) )
await asyncio.gather(*tasks) await asyncio.gather(*tasks)
# State is set optimistically in the command above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_write_ha_state()
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn device on.""" """Turn device on."""
await self._device.switch_on(set_status=True) await self.execute_device_command(
# State is set optimistically in the command above, therefore update Capability.SWITCH,
# the entity state ahead of receiving the confirming push updates Command.ON,
self.async_write_ha_state() )
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn device off.""" """Turn device off."""
await self._device.switch_off(set_status=True) await self.execute_device_command(
# State is set optimistically in the command above, therefore update Capability.SWITCH,
# the entity state ahead of receiving the confirming push updates Command.OFF,
self.async_write_ha_state() )
async def async_update(self) -> None:
"""Update the calculated fields of the AC."""
modes = {HVACMode.OFF}
for mode in self._device.status.supported_ac_modes:
if (state := AC_MODE_TO_STATE.get(mode)) is not None:
modes.add(state)
else:
_LOGGER.debug(
"Device %s (%s) returned an invalid supported AC mode: %s",
self._device.label,
self._device.device_id,
mode,
)
self._hvac_modes = list(modes)
@property @property
def current_temperature(self) -> float | None: def current_temperature(self) -> float | None:
"""Return the current temperature.""" """Return the current temperature."""
return self._device.status.temperature return self.get_attribute_value(
Capability.TEMPERATURE_MEASUREMENT, Attribute.TEMPERATURE
)
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]:
@ -465,100 +447,114 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
Include attributes from the Demand Response Load Control (drlc) Include attributes from the Demand Response Load Control (drlc)
and Power Consumption capabilities. and Power Consumption capabilities.
""" """
attributes = [ drlc_status = self.get_attribute_value(
"drlc_status_duration", Capability.DEMAND_RESPONSE_LOAD_CONTROL,
"drlc_status_level", Attribute.DEMAND_RESPONSE_LOAD_CONTROL_STATUS,
"drlc_status_start", )
"drlc_status_override", return {
] "drlc_status_duration": drlc_status["duration"],
state_attributes = {} "drlc_status_level": drlc_status["drlcLevel"],
for attribute in attributes: "drlc_status_start": drlc_status["start"],
value = getattr(self._device.status, attribute) "drlc_status_override": drlc_status["override"],
if value is not None: }
state_attributes[attribute] = value
return state_attributes
@property @property
def fan_mode(self) -> str: def fan_mode(self) -> str:
"""Return the fan setting.""" """Return the fan setting."""
return self._device.status.fan_mode return self.get_attribute_value(
Capability.AIR_CONDITIONER_FAN_MODE, Attribute.FAN_MODE
)
@property @property
def fan_modes(self) -> list[str]: def fan_modes(self) -> list[str]:
"""Return the list of available fan modes.""" """Return the list of available fan modes."""
return self._device.status.supported_ac_fan_modes return self.get_attribute_value(
Capability.AIR_CONDITIONER_FAN_MODE, Attribute.SUPPORTED_AC_FAN_MODES
)
@property @property
def hvac_mode(self) -> HVACMode | None: def hvac_mode(self) -> HVACMode | None:
"""Return current operation ie. heat, cool, idle.""" """Return current operation ie. heat, cool, idle."""
if not self._device.status.switch: if self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "off":
return HVACMode.OFF return HVACMode.OFF
return AC_MODE_TO_STATE.get(self._device.status.air_conditioner_mode) return AC_MODE_TO_STATE.get(
self.get_attribute_value(
@property Capability.AIR_CONDITIONER_MODE, Attribute.AIR_CONDITIONER_MODE
def hvac_modes(self) -> list[HVACMode]: )
"""Return the list of available operation modes.""" )
return self._hvac_modes
@property @property
def target_temperature(self) -> float: def target_temperature(self) -> float:
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
return self._device.status.cooling_setpoint return self.get_attribute_value(
Capability.THERMOSTAT_COOLING_SETPOINT, Attribute.COOLING_SETPOINT
)
@property @property
def temperature_unit(self) -> str: def temperature_unit(self) -> str:
"""Return the unit of measurement.""" """Return the unit of measurement."""
return UNIT_MAP[self._device.status.attributes[Attribute.temperature].unit] unit = self._internal_state[Capability.TEMPERATURE_MEASUREMENT][
Attribute.TEMPERATURE
].unit
assert unit
return UNIT_MAP[unit]
def _determine_swing_modes(self) -> list[str] | None: def _determine_swing_modes(self) -> list[str] | None:
"""Return the list of available swing modes.""" """Return the list of available swing modes."""
supported_swings = None if (
supported_modes = self._device.status.attributes[ supported_modes := self.get_attribute_value(
Attribute.supported_fan_oscillation_modes Capability.FAN_OSCILLATION_MODE,
][0] Attribute.SUPPORTED_FAN_OSCILLATION_MODES,
if supported_modes is not None: )
supported_swings = [ ) is None:
FAN_OSCILLATION_TO_SWING.get(m, SWING_OFF) for m in supported_modes return None
] return [FAN_OSCILLATION_TO_SWING.get(m, SWING_OFF) for m in supported_modes]
return supported_swings
async def async_set_swing_mode(self, swing_mode: str) -> None: async def async_set_swing_mode(self, swing_mode: str) -> None:
"""Set swing mode.""" """Set swing mode."""
fan_oscillation_mode = SWING_TO_FAN_OSCILLATION[swing_mode] await self.execute_device_command(
await self._device.set_fan_oscillation_mode(fan_oscillation_mode) Capability.FAN_OSCILLATION_MODE,
Command.SET_FAN_OSCILLATION_MODE,
# setting the fan must reset the preset mode (it deactivates the windFree function) argument=SWING_TO_FAN_OSCILLATION[swing_mode],
self._attr_preset_mode = None )
self.async_schedule_update_ha_state(True)
@property @property
def swing_mode(self) -> str: def swing_mode(self) -> str:
"""Return the swing setting.""" """Return the swing setting."""
return FAN_OSCILLATION_TO_SWING.get( return FAN_OSCILLATION_TO_SWING.get(
self._device.status.fan_oscillation_mode, SWING_OFF self.get_attribute_value(
Capability.FAN_OSCILLATION_MODE, Attribute.FAN_OSCILLATION_MODE
),
SWING_OFF,
) )
def _determine_preset_modes(self) -> list[str] | None: def _determine_preset_modes(self) -> list[str] | None:
"""Return a list of available preset modes.""" """Return a list of available preset modes."""
supported_modes: list | None = self._device.status.attributes[ if self.supports_capability(Capability.CUSTOM_AIR_CONDITIONER_OPTIONAL_MODE):
"supportedAcOptionalMode" supported_modes = self.get_attribute_value(
].value Capability.CUSTOM_AIR_CONDITIONER_OPTIONAL_MODE,
if supported_modes and WINDFREE in supported_modes: Attribute.SUPPORTED_AC_OPTIONAL_MODE,
return [WINDFREE] )
if supported_modes and WINDFREE in supported_modes:
return [WINDFREE]
return None return None
async def async_set_preset_mode(self, preset_mode: str) -> None: async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set special modes (currently only windFree is supported).""" """Set special modes (currently only windFree is supported)."""
result = await self._device.command( await self.execute_device_command(
"main", Capability.CUSTOM_AIR_CONDITIONER_OPTIONAL_MODE,
"custom.airConditionerOptionalMode", Command.SET_AC_OPTIONAL_MODE,
"setAcOptionalMode", argument=preset_mode,
[preset_mode],
) )
if result:
self._device.status.update_attribute_value("acOptionalMode", preset_mode)
self._attr_preset_mode = preset_mode def _determine_hvac_modes(self) -> list[HVACMode]:
"""Determine the supported HVAC modes."""
self.async_write_ha_state() modes = [HVACMode.OFF]
modes.extend(
state
for mode in self.get_attribute_value(
Capability.AIR_CONDITIONER_MODE, Attribute.SUPPORTED_AC_MODES
)
if (state := AC_MODE_TO_STATE.get(mode)) is not None
)
return modes

View File

@ -1,298 +1,83 @@
"""Config flow to configure SmartThings.""" """Config flow to configure SmartThings."""
from collections.abc import Mapping from collections.abc import Mapping
from http import HTTPStatus
import logging import logging
from typing import Any from typing import Any
from aiohttp import ClientResponseError from pysmartthings import SmartThings
from pysmartthings import APIResponseError, AppOAuth, SmartThings
from pysmartthings.installedapp import format_install_url
import voluptuous as vol
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
from .const import ( from .const import CONF_LOCATION_ID, DOMAIN, OLD_DATA, SCOPES
APP_OAUTH_CLIENT_NAME,
APP_OAUTH_SCOPES,
CONF_APP_ID,
CONF_INSTALLED_APP_ID,
CONF_LOCATION_ID,
CONF_REFRESH_TOKEN,
DOMAIN,
VAL_UID_MATCHER,
)
from .smartapp import (
create_app,
find_app,
format_unique_id,
get_webhook_url,
setup_smartapp,
setup_smartapp_endpoint,
update_app,
validate_webhook_requirements,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class SmartThingsFlowHandler(ConfigFlow, domain=DOMAIN): class SmartThingsConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Handle configuration of SmartThings integrations.""" """Handle configuration of SmartThings integrations."""
VERSION = 2 VERSION = 3
DOMAIN = DOMAIN
api: SmartThings @property
app_id: str def logger(self) -> logging.Logger:
location_id: str """Return logger."""
return logging.getLogger(__name__)
def __init__(self) -> None: @property
"""Create a new instance of the flow handler.""" def extra_authorize_data(self) -> dict[str, Any]:
self.access_token: str | None = None """Extra data that needs to be appended to the authorize url."""
self.oauth_client_secret = None return {"scope": " ".join(SCOPES)}
self.oauth_client_id = None
self.installed_app_id = None
self.refresh_token = None
self.endpoints_initialized = False
async def async_step_import(self, import_data: None) -> ConfigFlowResult: async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Occurs when a previously entry setup fails and is re-initiated.""" """Create an entry for SmartThings."""
return await self.async_step_user(import_data) client = SmartThings(session=async_get_clientsession(self.hass))
client.authenticate(data[CONF_TOKEN][CONF_ACCESS_TOKEN])
locations = await client.get_locations()
location = locations[0]
# We pick to use the location id as unique id rather than the installed app id
# as the installed app id could change with the right settings in the SmartApp
# or the app used to sign in changed for any reason.
await self.async_set_unique_id(location.location_id)
if self.source != SOURCE_REAUTH:
self._abort_if_unique_id_configured()
async def async_step_user( return self.async_create_entry(
self, user_input: dict[str, Any] | None = None title=location.name,
) -> ConfigFlowResult: data={**data, CONF_LOCATION_ID: location.location_id},
"""Validate and confirm webhook setup."""
if not self.endpoints_initialized:
self.endpoints_initialized = True
await setup_smartapp_endpoint(
self.hass, len(self._async_current_entries()) == 0
) )
webhook_url = get_webhook_url(self.hass)
# Abort if the webhook is invalid if (entry := self._get_reauth_entry()) and CONF_TOKEN not in entry.data:
if not validate_webhook_requirements(self.hass): if entry.data[OLD_DATA][CONF_LOCATION_ID] != location.location_id:
return self.async_abort( return self.async_abort(reason="reauth_location_mismatch")
reason="invalid_webhook_url", return self.async_update_reload_and_abort(
description_placeholders={ self._get_reauth_entry(),
"webhook_url": webhook_url, data_updates={
"component_url": ( **data,
"https://www.home-assistant.io/integrations/smartthings/" CONF_LOCATION_ID: location.location_id,
),
}, },
unique_id=location.location_id,
) )
self._abort_if_unique_id_mismatch(reason="reauth_account_mismatch")
# Show the confirmation return self.async_update_reload_and_abort(
if user_input is None: self._get_reauth_entry(), data_updates=data
return self.async_show_form(
step_id="user",
description_placeholders={"webhook_url": webhook_url},
)
# Show the next screen
return await self.async_step_pat()
async def async_step_pat(
self, user_input: dict[str, str] | None = None
) -> ConfigFlowResult:
"""Get the Personal Access Token and validate it."""
errors: dict[str, str] = {}
if user_input is None or CONF_ACCESS_TOKEN not in user_input:
return self._show_step_pat(errors)
self.access_token = user_input[CONF_ACCESS_TOKEN]
# Ensure token is a UUID
if not VAL_UID_MATCHER.match(self.access_token):
errors[CONF_ACCESS_TOKEN] = "token_invalid_format"
return self._show_step_pat(errors)
# Setup end-point
self.api = SmartThings(async_get_clientsession(self.hass), self.access_token)
try:
app = await find_app(self.hass, self.api)
if app:
await app.refresh() # load all attributes
await update_app(self.hass, app)
# Find an existing entry to copy the oauth client
existing = next(
(
entry
for entry in self._async_current_entries()
if entry.data[CONF_APP_ID] == app.app_id
),
None,
)
if existing:
self.oauth_client_id = existing.data[CONF_CLIENT_ID]
self.oauth_client_secret = existing.data[CONF_CLIENT_SECRET]
else:
# Get oauth client id/secret by regenerating it
app_oauth = AppOAuth(app.app_id)
app_oauth.client_name = APP_OAUTH_CLIENT_NAME
app_oauth.scope.extend(APP_OAUTH_SCOPES)
client = await self.api.generate_app_oauth(app_oauth)
self.oauth_client_secret = client.client_secret
self.oauth_client_id = client.client_id
else:
app, client = await create_app(self.hass, self.api)
self.oauth_client_secret = client.client_secret
self.oauth_client_id = client.client_id
setup_smartapp(self.hass, app)
self.app_id = app.app_id
except APIResponseError as ex:
if ex.is_target_error():
errors["base"] = "webhook_error"
else:
errors["base"] = "app_setup_error"
_LOGGER.exception(
"API error setting up the SmartApp: %s", ex.raw_error_response
)
return self._show_step_pat(errors)
except ClientResponseError as ex:
if ex.status == HTTPStatus.UNAUTHORIZED:
errors[CONF_ACCESS_TOKEN] = "token_unauthorized"
_LOGGER.debug(
"Unauthorized error received setting up SmartApp", exc_info=True
)
elif ex.status == HTTPStatus.FORBIDDEN:
errors[CONF_ACCESS_TOKEN] = "token_forbidden"
_LOGGER.debug(
"Forbidden error received setting up SmartApp", exc_info=True
)
else:
errors["base"] = "app_setup_error"
_LOGGER.exception("Unexpected error setting up the SmartApp")
return self._show_step_pat(errors)
except Exception:
errors["base"] = "app_setup_error"
_LOGGER.exception("Unexpected error setting up the SmartApp")
return self._show_step_pat(errors)
return await self.async_step_select_location()
async def async_step_select_location(
self, user_input: dict[str, str] | None = None
) -> ConfigFlowResult:
"""Ask user to select the location to setup."""
if user_input is None or CONF_LOCATION_ID not in user_input:
# Get available locations
existing_locations = [
entry.data[CONF_LOCATION_ID] for entry in self._async_current_entries()
]
locations = await self.api.locations()
locations_options = {
location.location_id: location.name
for location in locations
if location.location_id not in existing_locations
}
if not locations_options:
return self.async_abort(reason="no_available_locations")
return self.async_show_form(
step_id="select_location",
data_schema=vol.Schema(
{vol.Required(CONF_LOCATION_ID): vol.In(locations_options)}
),
)
self.location_id = user_input[CONF_LOCATION_ID]
await self.async_set_unique_id(format_unique_id(self.app_id, self.location_id))
return await self.async_step_authorize()
async def async_step_authorize(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Wait for the user to authorize the app installation."""
user_input = {} if user_input is None else user_input
self.installed_app_id = user_input.get(CONF_INSTALLED_APP_ID)
self.refresh_token = user_input.get(CONF_REFRESH_TOKEN)
if self.installed_app_id is None:
# Launch the external setup URL
url = format_install_url(self.app_id, self.location_id)
return self.async_external_step(step_id="authorize", url=url)
next_step_id = "install"
if self.source == SOURCE_REAUTH:
next_step_id = "update"
return self.async_external_step_done(next_step_id=next_step_id)
def _show_step_pat(self, errors):
if self.access_token is None:
# Get the token from an existing entry to make it easier to setup multiple locations.
self.access_token = next(
(
entry.data.get(CONF_ACCESS_TOKEN)
for entry in self._async_current_entries()
),
None,
)
return self.async_show_form(
step_id="pat",
data_schema=vol.Schema(
{vol.Required(CONF_ACCESS_TOKEN, default=self.access_token): str}
),
errors=errors,
description_placeholders={
"token_url": "https://account.smartthings.com/tokens",
"component_url": (
"https://www.home-assistant.io/integrations/smartthings/"
),
},
) )
async def async_step_reauth( async def async_step_reauth(
self, entry_data: Mapping[str, Any] self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Handle re-authentication of an existing config entry.""" """Perform reauth upon migration of old entries."""
return await self.async_step_reauth_confirm() return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm( async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Handle re-authentication of an existing config entry.""" """Confirm reauth dialog."""
if user_input is None: if user_input is None:
return self.async_show_form(step_id="reauth_confirm") return self.async_show_form(
self.app_id = self._get_reauth_entry().data[CONF_APP_ID] step_id="reauth_confirm",
self.location_id = self._get_reauth_entry().data[CONF_LOCATION_ID] )
self._set_confirm_only() return await self.async_step_user()
return await self.async_step_authorize()
async def async_step_update(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle re-authentication of an existing config entry."""
return await self.async_step_update_confirm()
async def async_step_update_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle re-authentication of an existing config entry."""
if user_input is None:
self._set_confirm_only()
return self.async_show_form(step_id="update_confirm")
entry = self._get_reauth_entry()
return self.async_update_reload_and_abort(
entry, data_updates={CONF_REFRESH_TOKEN: self.refresh_token}
)
async def async_step_install(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Create a config entry at completion of a flow and authorization of the app."""
data = {
CONF_ACCESS_TOKEN: self.access_token,
CONF_REFRESH_TOKEN: self.refresh_token,
CONF_CLIENT_ID: self.oauth_client_id,
CONF_CLIENT_SECRET: self.oauth_client_secret,
CONF_LOCATION_ID: self.location_id,
CONF_APP_ID: self.app_id,
CONF_INSTALLED_APP_ID: self.installed_app_id,
}
location = await self.api.location(data[CONF_LOCATION_ID])
return self.async_create_entry(title=location.name, data=data)

View File

@ -1,15 +1,23 @@
"""Constants used by the SmartThings component and platforms.""" """Constants used by the SmartThings component and platforms."""
from datetime import timedelta
import re
from homeassistant.const import Platform
DOMAIN = "smartthings" DOMAIN = "smartthings"
APP_OAUTH_CLIENT_NAME = "Home Assistant" SCOPES = [
APP_OAUTH_SCOPES = ["r:devices:*"] "r:devices:*",
APP_NAME_PREFIX = "homeassistant." "w:devices:*",
"x:devices:*",
"r:hubs:*",
"r:locations:*",
"w:locations:*",
"x:locations:*",
"r:scenes:*",
"x:scenes:*",
"r:rules:*",
"w:rules:*",
"r:installedapps",
"w:installedapps",
"sse",
]
CONF_APP_ID = "app_id" CONF_APP_ID = "app_id"
CONF_CLOUDHOOK_URL = "cloudhook_url" CONF_CLOUDHOOK_URL = "cloudhook_url"
@ -18,41 +26,5 @@ CONF_INSTANCE_ID = "instance_id"
CONF_LOCATION_ID = "location_id" CONF_LOCATION_ID = "location_id"
CONF_REFRESH_TOKEN = "refresh_token" CONF_REFRESH_TOKEN = "refresh_token"
DATA_MANAGER = "manager" MAIN = "main"
DATA_BROKERS = "brokers" OLD_DATA = "old_data"
EVENT_BUTTON = "smartthings.button"
SIGNAL_SMARTTHINGS_UPDATE = "smartthings_update"
SIGNAL_SMARTAPP_PREFIX = "smartthings_smartap_"
SETTINGS_INSTANCE_ID = "hassInstanceId"
SUBSCRIPTION_WARNING_LIMIT = 40
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
# Ordered 'specific to least-specific platform' in order for capabilities
# to be drawn-down and represented by the most appropriate platform.
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.CLIMATE,
Platform.COVER,
Platform.FAN,
Platform.LIGHT,
Platform.LOCK,
Platform.SCENE,
Platform.SENSOR,
Platform.SWITCH,
]
IGNORED_CAPABILITIES = [
"execute",
"healthCheck",
"ocf",
]
TOKEN_REFRESH_INTERVAL = timedelta(days=14)
VAL_UID = "^(?:([0-9a-fA-F]{32})|([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}))$"
VAL_UID_MATCHER = re.compile(VAL_UID)

View File

@ -2,25 +2,23 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence
from typing import Any from typing import Any
from pysmartthings import Attribute, Capability from pysmartthings import Attribute, Capability, Command, SmartThings
from homeassistant.components.cover import ( from homeassistant.components.cover import (
ATTR_POSITION, ATTR_POSITION,
DOMAIN as COVER_DOMAIN,
CoverDeviceClass, CoverDeviceClass,
CoverEntity, CoverEntity,
CoverEntityFeature, CoverEntityFeature,
CoverState, CoverState,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
VALUE_TO_STATE = { VALUE_TO_STATE = {
@ -32,114 +30,99 @@ VALUE_TO_STATE = {
"unknown": None, "unknown": None,
} }
CAPABILITIES = (Capability.WINDOW_SHADE, Capability.DOOR_CONTROL)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add covers for a config entry.""" """Add covers for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] entry_data = entry.runtime_data
async_add_entities( async_add_entities(
[ SmartThingsCover(entry_data.client, device, capability)
SmartThingsCover(device) for device in entry_data.devices.values()
for device in broker.devices.values() for capability in device.status[MAIN]
if broker.any_assigned(device.device_id, COVER_DOMAIN) if capability in CAPABILITIES
],
True,
) )
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
"""Return all capabilities supported if minimum required are present."""
min_required = [
Capability.door_control,
Capability.garage_door_control,
Capability.window_shade,
]
# Must have one of the min_required
if any(capability in capabilities for capability in min_required):
# Return all capabilities supported/consumed
return [
*min_required,
Capability.battery,
Capability.switch_level,
Capability.window_shade_level,
]
return None
class SmartThingsCover(SmartThingsEntity, CoverEntity): class SmartThingsCover(SmartThingsEntity, CoverEntity):
"""Define a SmartThings cover.""" """Define a SmartThings cover."""
def __init__(self, device): _state: CoverState | None = None
def __init__(
self, client: SmartThings, device: FullDevice, capability: Capability
) -> None:
"""Initialize the cover class.""" """Initialize the cover class."""
super().__init__(device) super().__init__(
self._current_cover_position = None client,
self._state = None device,
{
capability,
Capability.BATTERY,
Capability.WINDOW_SHADE_LEVEL,
Capability.SWITCH_LEVEL,
},
)
self.capability = capability
self._attr_supported_features = ( self._attr_supported_features = (
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
) )
if ( if self.supports_capability(Capability.WINDOW_SHADE_LEVEL):
Capability.switch_level in device.capabilities self.level_capability = Capability.WINDOW_SHADE_LEVEL
or Capability.window_shade_level in device.capabilities self.level_command = Command.SET_SHADE_LEVEL
): else:
self.level_capability = Capability.SWITCH_LEVEL
self.level_command = Command.SET_LEVEL
if self.supports_capability(
Capability.SWITCH_LEVEL
) or self.supports_capability(Capability.WINDOW_SHADE_LEVEL):
self._attr_supported_features |= CoverEntityFeature.SET_POSITION self._attr_supported_features |= CoverEntityFeature.SET_POSITION
if Capability.door_control in device.capabilities: if self.supports_capability(Capability.DOOR_CONTROL):
self._attr_device_class = CoverDeviceClass.DOOR self._attr_device_class = CoverDeviceClass.DOOR
elif Capability.window_shade in device.capabilities: elif self.supports_capability(Capability.WINDOW_SHADE):
self._attr_device_class = CoverDeviceClass.SHADE self._attr_device_class = CoverDeviceClass.SHADE
elif Capability.garage_door_control in device.capabilities:
self._attr_device_class = CoverDeviceClass.GARAGE
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close cover.""" """Close cover."""
# Same command for all 3 supported capabilities await self.execute_device_command(self.capability, Command.CLOSE)
await self._device.close(set_status=True)
# State is set optimistically in the commands above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_schedule_update_ha_state(True)
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover.""" """Open the cover."""
# Same for all capability types await self.execute_device_command(self.capability, Command.OPEN)
await self._device.open(set_status=True)
# State is set optimistically in the commands above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_schedule_update_ha_state(True)
async def async_set_cover_position(self, **kwargs: Any) -> None: async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position.""" """Move the cover to a specific position."""
if not self.supported_features & CoverEntityFeature.SET_POSITION: await self.execute_device_command(
return self.level_capability,
# Do not set_status=True as device will report progress. self.level_command,
if Capability.window_shade_level in self._device.capabilities: argument=kwargs[ATTR_POSITION],
await self._device.set_window_shade_level( )
kwargs[ATTR_POSITION], set_status=False
)
else:
await self._device.set_level(kwargs[ATTR_POSITION], set_status=False)
async def async_update(self) -> None: def _update_attr(self) -> None:
"""Update the attrs of the cover.""" """Update the attrs of the cover."""
if Capability.door_control in self._device.capabilities: attribute = {
self._state = VALUE_TO_STATE.get(self._device.status.door) Capability.WINDOW_SHADE: Attribute.WINDOW_SHADE,
elif Capability.window_shade in self._device.capabilities: Capability.DOOR_CONTROL: Attribute.DOOR,
self._state = VALUE_TO_STATE.get(self._device.status.window_shade) }[self.capability]
elif Capability.garage_door_control in self._device.capabilities: self._state = VALUE_TO_STATE.get(
self._state = VALUE_TO_STATE.get(self._device.status.door) self.get_attribute_value(self.capability, attribute)
)
if Capability.window_shade_level in self._device.capabilities: if self.supports_capability(Capability.SWITCH_LEVEL):
self._attr_current_cover_position = self._device.status.shade_level self._attr_current_cover_position = self.get_attribute_value(
elif Capability.switch_level in self._device.capabilities: Capability.SWITCH_LEVEL, Attribute.LEVEL
self._attr_current_cover_position = self._device.status.level )
self._attr_extra_state_attributes = {} self._attr_extra_state_attributes = {}
battery = self._device.status.attributes[Attribute.battery].value if self.supports_capability(Capability.BATTERY):
if battery is not None: self._attr_extra_state_attributes[ATTR_BATTERY_LEVEL] = (
self._attr_extra_state_attributes[ATTR_BATTERY_LEVEL] = battery self.get_attribute_value(Capability.BATTERY, Attribute.BATTERY)
)
@property @property
def is_opening(self) -> bool: def is_opening(self) -> bool:

View File

@ -2,13 +2,15 @@
from __future__ import annotations from __future__ import annotations
from pysmartthings.device import DeviceEntity from typing import Any, cast
from pysmartthings import Attribute, Capability, Command, DeviceEvent, SmartThings
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from .const import DOMAIN, SIGNAL_SMARTTHINGS_UPDATE from . import FullDevice
from .const import DOMAIN, MAIN
class SmartThingsEntity(Entity): class SmartThingsEntity(Entity):
@ -16,35 +18,86 @@ class SmartThingsEntity(Entity):
_attr_should_poll = False _attr_should_poll = False
def __init__(self, device: DeviceEntity) -> None: def __init__(
self, client: SmartThings, device: FullDevice, capabilities: set[Capability]
) -> None:
"""Initialize the instance.""" """Initialize the instance."""
self._device = device self.client = client
self._dispatcher_remove = None self.capabilities = capabilities
self._attr_name = device.label self._internal_state = {
self._attr_unique_id = device.device_id capability: device.status[MAIN][capability]
for capability in capabilities
if capability in device.status[MAIN]
}
self.device = device
self._attr_name = device.device.label
self._attr_unique_id = device.device.device_id
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
configuration_url="https://account.smartthings.com", configuration_url="https://account.smartthings.com",
identifiers={(DOMAIN, device.device_id)}, identifiers={(DOMAIN, device.device.device_id)},
manufacturer=device.status.ocf_manufacturer_name, name=device.device.label,
model=device.status.ocf_model_number,
name=device.label,
hw_version=device.status.ocf_hardware_version,
sw_version=device.status.ocf_firmware_version,
) )
if (ocf := device.status[MAIN].get(Capability.OCF)) is not None:
self._attr_device_info.update(
{
"manufacturer": cast(
str | None, ocf[Attribute.MANUFACTURER_NAME].value
),
"model": cast(str | None, ocf[Attribute.MODEL_NUMBER].value),
"hw_version": cast(
str | None, ocf[Attribute.HARDWARE_VERSION].value
),
"sw_version": cast(
str | None, ocf[Attribute.OCF_FIRMWARE_VERSION].value
),
}
)
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Device added to hass.""" """Subscribe to updates."""
await super().async_added_to_hass()
for capability in self._internal_state:
self.async_on_remove(
self.client.add_device_event_listener(
self.device.device.device_id,
MAIN,
capability,
self._update_handler,
)
)
self._update_attr()
async def async_update_state(devices): def _update_handler(self, event: DeviceEvent) -> None:
"""Update device state.""" self._internal_state[event.capability][event.attribute].value = event.value
if self._device.device_id in devices: self._internal_state[event.capability][event.attribute].data = event.data
await self.async_update_ha_state(True) self._handle_update()
self._dispatcher_remove = async_dispatcher_connect( def supports_capability(self, capability: Capability) -> bool:
self.hass, SIGNAL_SMARTTHINGS_UPDATE, async_update_state """Test if device supports a capability."""
return capability in self.device.status[MAIN]
def get_attribute_value(self, capability: Capability, attribute: Attribute) -> Any:
"""Get the value of a device attribute."""
return self._internal_state[capability][attribute].value
def _update_attr(self) -> None:
"""Update the attributes."""
def _handle_update(self) -> None:
"""Handle updated data from the coordinator."""
self._update_attr()
self.async_write_ha_state()
async def execute_device_command(
self,
capability: Capability,
command: Command,
argument: int | str | list[Any] | dict[str, Any] | None = None,
) -> None:
"""Execute a command on the device."""
kwargs = {}
if argument is not None:
kwargs["argument"] = argument
await self.client.execute_device_command(
self.device.device.device_id, capability, command, MAIN, **kwargs
) )
async def async_will_remove_from_hass(self) -> None:
"""Disconnect the device when removed."""
if self._dispatcher_remove:
self._dispatcher_remove()

View File

@ -2,14 +2,12 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence
import math import math
from typing import Any from typing import Any
from pysmartthings import Capability from pysmartthings import Attribute, Capability, Command, SmartThings
from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.percentage import ( from homeassistant.util.percentage import (
@ -18,7 +16,8 @@ from homeassistant.util.percentage import (
) )
from homeassistant.util.scaling import int_states_in_range from homeassistant.util.scaling import int_states_in_range
from .const import DATA_BROKERS, DOMAIN from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
SPEED_RANGE = (1, 3) # off is not included SPEED_RANGE = (1, 3) # off is not included
@ -26,86 +25,73 @@ SPEED_RANGE = (1, 3) # off is not included
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add fans for a config entry.""" """Add fans for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] entry_data = entry.runtime_data
async_add_entities( async_add_entities(
SmartThingsFan(device) SmartThingsFan(entry_data.client, device)
for device in broker.devices.values() for device in entry_data.devices.values()
if broker.any_assigned(device.device_id, "fan") if Capability.SWITCH in device.status[MAIN]
and any(
capability in device.status[MAIN]
for capability in (
Capability.FAN_SPEED,
Capability.AIR_CONDITIONER_FAN_MODE,
)
)
and Capability.THERMOSTAT_COOLING_SETPOINT not in device.status[MAIN]
) )
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
"""Return all capabilities supported if minimum required are present."""
# MUST support switch as we need a way to turn it on and off
if Capability.switch not in capabilities:
return None
# These are all optional but at least one must be supported
optional = [
Capability.air_conditioner_fan_mode,
Capability.fan_speed,
]
# At least one of the optional capabilities must be supported
# to classify this entity as a fan.
# If they are not then return None and don't setup the platform.
if not any(capability in capabilities for capability in optional):
return None
supported = [Capability.switch]
supported.extend(
capability for capability in optional if capability in capabilities
)
return supported
class SmartThingsFan(SmartThingsEntity, FanEntity): class SmartThingsFan(SmartThingsEntity, FanEntity):
"""Define a SmartThings Fan.""" """Define a SmartThings Fan."""
_attr_speed_count = int_states_in_range(SPEED_RANGE) _attr_speed_count = int_states_in_range(SPEED_RANGE)
def __init__(self, device): def __init__(self, client: SmartThings, device: FullDevice) -> None:
"""Init the class.""" """Init the class."""
super().__init__(device) super().__init__(
client,
device,
{
Capability.SWITCH,
Capability.FAN_SPEED,
Capability.AIR_CONDITIONER_FAN_MODE,
},
)
self._attr_supported_features = self._determine_features() self._attr_supported_features = self._determine_features()
def _determine_features(self): def _determine_features(self):
flags = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON flags = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
if self._device.get_capability(Capability.fan_speed): if self.supports_capability(Capability.FAN_SPEED):
flags |= FanEntityFeature.SET_SPEED flags |= FanEntityFeature.SET_SPEED
if self._device.get_capability(Capability.air_conditioner_fan_mode): if self.supports_capability(Capability.AIR_CONDITIONER_FAN_MODE):
flags |= FanEntityFeature.PRESET_MODE flags |= FanEntityFeature.PRESET_MODE
return flags return flags
async def async_set_percentage(self, percentage: int) -> None: async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed percentage of the fan.""" """Set the speed percentage of the fan."""
await self._async_set_percentage(percentage) if percentage == 0:
await self.execute_device_command(Capability.SWITCH, Command.OFF)
async def _async_set_percentage(self, percentage: int | None) -> None:
if percentage is None:
await self._device.switch_on(set_status=True)
elif percentage == 0:
await self._device.switch_off(set_status=True)
else: else:
value = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage)) value = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
await self._device.set_fan_speed(value, set_status=True) await self.execute_device_command(
# State is set optimistically in the command above, therefore update Capability.FAN_SPEED,
# the entity state ahead of receiving the confirming push updates Command.SET_FAN_SPEED,
self.async_write_ha_state() argument=value,
)
async def async_set_preset_mode(self, preset_mode: str) -> None: async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset_mode of the fan.""" """Set the preset_mode of the fan."""
await self._device.set_fan_mode(preset_mode, set_status=True) await self.execute_device_command(
self.async_write_ha_state() Capability.AIR_CONDITIONER_FAN_MODE,
Command.SET_FAN_MODE,
argument=preset_mode,
)
async def async_turn_on( async def async_turn_on(
self, self,
@ -114,32 +100,30 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Turn the fan on.""" """Turn the fan on."""
if FanEntityFeature.SET_SPEED in self._attr_supported_features: if (
# If speed is set in features then turn the fan on with the speed. FanEntityFeature.SET_SPEED in self._attr_supported_features
await self._async_set_percentage(percentage) and percentage is not None
):
await self.async_set_percentage(percentage)
else: else:
# If speed is not valid then turn on the fan with the await self.execute_device_command(Capability.SWITCH, Command.ON)
await self._device.switch_on(set_status=True)
# State is set optimistically in the command above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_write_ha_state()
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the fan off.""" """Turn the fan off."""
await self._device.switch_off(set_status=True) await self.execute_device_command(Capability.SWITCH, Command.OFF)
# State is set optimistically in the command above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_write_ha_state()
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return true if fan is on.""" """Return true if fan is on."""
return self._device.status.switch return self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH)
@property @property
def percentage(self) -> int | None: def percentage(self) -> int | None:
"""Return the current speed percentage.""" """Return the current speed percentage."""
return ranged_value_to_percentage(SPEED_RANGE, self._device.status.fan_speed) return ranged_value_to_percentage(
SPEED_RANGE,
self.get_attribute_value(Capability.FAN_SPEED, Attribute.FAN_SPEED),
)
@property @property
def preset_mode(self) -> str | None: def preset_mode(self) -> str | None:
@ -147,7 +131,9 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
Requires FanEntityFeature.PRESET_MODE. Requires FanEntityFeature.PRESET_MODE.
""" """
return self._device.status.fan_mode return self.get_attribute_value(
Capability.AIR_CONDITIONER_FAN_MODE, Attribute.FAN_MODE
)
@property @property
def preset_modes(self) -> list[str] | None: def preset_modes(self) -> list[str] | None:
@ -155,4 +141,6 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
Requires FanEntityFeature.PRESET_MODE. Requires FanEntityFeature.PRESET_MODE.
""" """
return self._device.status.supported_ac_fan_modes return self.get_attribute_value(
Capability.AIR_CONDITIONER_FAN_MODE, Attribute.SUPPORTED_AC_FAN_MODES
)

View File

@ -3,10 +3,9 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Sequence
from typing import Any from typing import Any
from pysmartthings import Capability from pysmartthings import Attribute, Capability, Command, SmartThings
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
@ -18,54 +17,38 @@ from homeassistant.components.light import (
LightEntityFeature, LightEntityFeature,
brightness_supported, brightness_supported,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
CAPABILITIES = (
Capability.SWITCH_LEVEL,
Capability.COLOR_CONTROL,
Capability.COLOR_TEMPERATURE,
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add lights for a config entry.""" """Add lights for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] entry_data = entry.runtime_data
async_add_entities( async_add_entities(
[ SmartThingsLight(entry_data.client, device)
SmartThingsLight(device) for device in entry_data.devices.values()
for device in broker.devices.values() if Capability.SWITCH in device.status[MAIN]
if broker.any_assigned(device.device_id, "light") and any(capability in device.status[MAIN] for capability in CAPABILITIES)
],
True,
) )
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None: def convert_scale(
"""Return all capabilities supported if minimum required are present.""" value: float, value_scale: int, target_scale: int, round_digits: int = 4
supported = [ ) -> float:
Capability.switch,
Capability.switch_level,
Capability.color_control,
Capability.color_temperature,
]
# Must be able to be turned on/off.
if Capability.switch not in capabilities:
return None
# Must have one of these
light_capabilities = [
Capability.color_control,
Capability.color_temperature,
Capability.switch_level,
]
if any(capability in capabilities for capability in light_capabilities):
return supported
return None
def convert_scale(value, value_scale, target_scale, round_digits=4):
"""Convert a value to a different scale.""" """Convert a value to a different scale."""
return round(value * target_scale / value_scale, round_digits) return round(value * target_scale / value_scale, round_digits)
@ -76,46 +59,41 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
_attr_supported_color_modes: set[ColorMode] _attr_supported_color_modes: set[ColorMode]
# SmartThings does not expose this attribute, instead it's # SmartThings does not expose this attribute, instead it's
# implemented within each device-type handler. This value is the # implemented within each device-type handler. This value is the
# lowest kelvin found supported across 20+ handlers. # lowest kelvin found supported across 20+ handlers.
_attr_min_color_temp_kelvin = 2000 # 500 mireds _attr_min_color_temp_kelvin = 2000 # 500 mireds
# SmartThings does not expose this attribute, instead it's # SmartThings does not expose this attribute, instead it's
# implemented within each device-type handler. This value is the # implemented within each device-type handler. This value is the
# highest kelvin found supported across 20+ handlers. # highest kelvin found supported across 20+ handlers.
_attr_max_color_temp_kelvin = 9000 # 111 mireds _attr_max_color_temp_kelvin = 9000 # 111 mireds
def __init__(self, device): def __init__(self, client: SmartThings, device: FullDevice) -> None:
"""Initialize a SmartThingsLight.""" """Initialize a SmartThingsLight."""
super().__init__(device) super().__init__(
self._attr_supported_color_modes = self._determine_color_modes() client,
self._attr_supported_features = self._determine_features() device,
{
def _determine_color_modes(self): Capability.COLOR_CONTROL,
"""Get features supported by the device.""" Capability.COLOR_TEMPERATURE,
Capability.SWITCH_LEVEL,
Capability.SWITCH,
},
)
color_modes = set() color_modes = set()
# Color Temperature if self.supports_capability(Capability.COLOR_TEMPERATURE):
if Capability.color_temperature in self._device.capabilities:
color_modes.add(ColorMode.COLOR_TEMP) color_modes.add(ColorMode.COLOR_TEMP)
# Color if self.supports_capability(Capability.COLOR_CONTROL):
if Capability.color_control in self._device.capabilities:
color_modes.add(ColorMode.HS) color_modes.add(ColorMode.HS)
# Brightness if not color_modes and self.supports_capability(Capability.SWITCH_LEVEL):
if not color_modes and Capability.switch_level in self._device.capabilities:
color_modes.add(ColorMode.BRIGHTNESS) color_modes.add(ColorMode.BRIGHTNESS)
if not color_modes: if not color_modes:
color_modes.add(ColorMode.ONOFF) color_modes.add(ColorMode.ONOFF)
self._attr_supported_color_modes = color_modes
return color_modes
def _determine_features(self) -> LightEntityFeature:
"""Get features supported by the device."""
features = LightEntityFeature(0) features = LightEntityFeature(0)
# Transition if self.supports_capability(Capability.SWITCH_LEVEL):
if Capability.switch_level in self._device.capabilities:
features |= LightEntityFeature.TRANSITION features |= LightEntityFeature.TRANSITION
self._attr_supported_features = features
return features
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on.""" """Turn the light on."""
@ -136,11 +114,10 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
kwargs[ATTR_BRIGHTNESS], kwargs.get(ATTR_TRANSITION, 0) kwargs[ATTR_BRIGHTNESS], kwargs.get(ATTR_TRANSITION, 0)
) )
else: else:
await self._device.switch_on(set_status=True) await self.execute_device_command(
Capability.SWITCH,
# State is set optimistically in the commands above, therefore update Command.ON,
# the entity state ahead of receiving the confirming push updates )
self.async_schedule_update_ha_state(True)
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
@ -148,27 +125,39 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
if ATTR_TRANSITION in kwargs: if ATTR_TRANSITION in kwargs:
await self.async_set_level(0, int(kwargs[ATTR_TRANSITION])) await self.async_set_level(0, int(kwargs[ATTR_TRANSITION]))
else: else:
await self._device.switch_off(set_status=True) await self.execute_device_command(
Capability.SWITCH,
Command.OFF,
)
# State is set optimistically in the commands above, therefore update def _update_attr(self) -> None:
# the entity state ahead of receiving the confirming push updates
self.async_schedule_update_ha_state(True)
async def async_update(self) -> None:
"""Update entity attributes when the device status has changed.""" """Update entity attributes when the device status has changed."""
# Brightness and transition # Brightness and transition
if brightness_supported(self._attr_supported_color_modes): if brightness_supported(self._attr_supported_color_modes):
self._attr_brightness = int( self._attr_brightness = int(
convert_scale(self._device.status.level, 100, 255, 0) convert_scale(
self.get_attribute_value(Capability.SWITCH_LEVEL, Attribute.LEVEL),
100,
255,
0,
)
) )
# Color Temperature # Color Temperature
if ColorMode.COLOR_TEMP in self._attr_supported_color_modes: if ColorMode.COLOR_TEMP in self._attr_supported_color_modes:
self._attr_color_temp_kelvin = self._device.status.color_temperature self._attr_color_temp_kelvin = self.get_attribute_value(
Capability.COLOR_TEMPERATURE, Attribute.COLOR_TEMPERATURE
)
# Color # Color
if ColorMode.HS in self._attr_supported_color_modes: if ColorMode.HS in self._attr_supported_color_modes:
self._attr_hs_color = ( self._attr_hs_color = (
convert_scale(self._device.status.hue, 100, 360), convert_scale(
self._device.status.saturation, self.get_attribute_value(Capability.COLOR_CONTROL, Attribute.HUE),
100,
360,
),
self.get_attribute_value(
Capability.COLOR_CONTROL, Attribute.SATURATION
),
) )
async def async_set_color(self, hs_color): async def async_set_color(self, hs_color):
@ -176,14 +165,22 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
hue = convert_scale(float(hs_color[0]), 360, 100) hue = convert_scale(float(hs_color[0]), 360, 100)
hue = max(min(hue, 100.0), 0.0) hue = max(min(hue, 100.0), 0.0)
saturation = max(min(float(hs_color[1]), 100.0), 0.0) saturation = max(min(float(hs_color[1]), 100.0), 0.0)
await self._device.set_color(hue, saturation, set_status=True) await self.execute_device_command(
Capability.COLOR_CONTROL,
Command.SET_COLOR,
argument={"hue": hue, "saturation": saturation},
)
async def async_set_color_temp(self, value: int): async def async_set_color_temp(self, value: int):
"""Set the color temperature of the device.""" """Set the color temperature of the device."""
kelvin = max(min(value, 30000), 1) kelvin = max(min(value, 30000), 1)
await self._device.set_color_temperature(kelvin, set_status=True) await self.execute_device_command(
Capability.COLOR_TEMPERATURE,
Command.SET_COLOR_TEMPERATURE,
argument=kelvin,
)
async def async_set_level(self, brightness: int, transition: int): async def async_set_level(self, brightness: int, transition: int) -> None:
"""Set the brightness of the light over transition.""" """Set the brightness of the light over transition."""
level = int(convert_scale(brightness, 255, 100, 0)) level = int(convert_scale(brightness, 255, 100, 0))
# Due to rounding, set level to 1 (one) so we don't inadvertently # Due to rounding, set level to 1 (one) so we don't inadvertently
@ -191,7 +188,11 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
level = 1 if level == 0 and brightness > 0 else level level = 1 if level == 0 and brightness > 0 else level
level = max(min(level, 100), 0) level = max(min(level, 100), 0)
duration = int(transition) duration = int(transition)
await self._device.set_level(level, duration, set_status=True) await self.execute_device_command(
Capability.SWITCH_LEVEL,
Command.SET_LEVEL,
argument=[level, duration],
)
@property @property
def color_mode(self) -> ColorMode: def color_mode(self) -> ColorMode:
@ -208,4 +209,4 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return true if light is on.""" """Return true if light is on."""
return self._device.status.switch return self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on"

View File

@ -2,17 +2,16 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence
from typing import Any from typing import Any
from pysmartthings import Attribute, Capability from pysmartthings import Attribute, Capability, Command
from homeassistant.components.lock import LockEntity from homeassistant.components.lock import LockEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
ST_STATE_LOCKED = "locked" ST_STATE_LOCKED = "locked"
@ -28,48 +27,47 @@ ST_LOCK_ATTR_MAP = {
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add locks for a config entry.""" """Add locks for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] entry_data = entry.runtime_data
async_add_entities( async_add_entities(
SmartThingsLock(device) SmartThingsLock(entry_data.client, device, {Capability.LOCK})
for device in broker.devices.values() for device in entry_data.devices.values()
if broker.any_assigned(device.device_id, "lock") if Capability.LOCK in device.status[MAIN]
) )
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
"""Return all capabilities supported if minimum required are present."""
if Capability.lock in capabilities:
return [Capability.lock]
return None
class SmartThingsLock(SmartThingsEntity, LockEntity): class SmartThingsLock(SmartThingsEntity, LockEntity):
"""Define a SmartThings lock.""" """Define a SmartThings lock."""
async def async_lock(self, **kwargs: Any) -> None: async def async_lock(self, **kwargs: Any) -> None:
"""Lock the device.""" """Lock the device."""
await self._device.lock(set_status=True) await self.execute_device_command(
self.async_write_ha_state() Capability.LOCK,
Command.LOCK,
)
async def async_unlock(self, **kwargs: Any) -> None: async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the device.""" """Unlock the device."""
await self._device.unlock(set_status=True) await self.execute_device_command(
self.async_write_ha_state() Capability.LOCK,
Command.UNLOCK,
)
@property @property
def is_locked(self) -> bool: def is_locked(self) -> bool:
"""Return true if lock is locked.""" """Return true if lock is locked."""
return self._device.status.lock == ST_STATE_LOCKED return (
self.get_attribute_value(Capability.LOCK, Attribute.LOCK) == ST_STATE_LOCKED
)
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]:
"""Return device specific state attributes.""" """Return device specific state attributes."""
state_attrs = {} state_attrs = {}
status = self._device.status.attributes[Attribute.lock] status = self._internal_state[Capability.LOCK][Attribute.LOCK]
if status.value: if status.value:
state_attrs["lock_state"] = status.value state_attrs["lock_state"] = status.value
if isinstance(status.data, dict): if isinstance(status.data, dict):

View File

@ -1,10 +1,9 @@
{ {
"domain": "smartthings", "domain": "smartthings",
"name": "SmartThings", "name": "SmartThings",
"after_dependencies": ["cloud"], "codeowners": ["@joostlek"],
"codeowners": [],
"config_flow": true, "config_flow": true,
"dependencies": ["webhook"], "dependencies": ["application_credentials"],
"dhcp": [ "dhcp": [
{ {
"hostname": "st*", "hostname": "st*",
@ -29,6 +28,6 @@
], ],
"documentation": "https://www.home-assistant.io/integrations/smartthings", "documentation": "https://www.home-assistant.io/integrations/smartthings",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["httpsig", "pysmartapp", "pysmartthings"], "loggers": ["pysmartthings"],
"requirements": ["pysmartapp==0.3.5", "pysmartthings==0.7.8"] "requirements": ["pysmartthings==1.2.0"]
} }

View File

@ -2,39 +2,42 @@
from typing import Any from typing import Any
from pysmartthings import Scene as STScene, SmartThings
from homeassistant.components.scene import Scene from homeassistant.components.scene import Scene
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import SmartThingsConfigEntry
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add switches for a config entry.""" """Add lights for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] client = entry.runtime_data.client
async_add_entities(SmartThingsScene(scene) for scene in broker.scenes.values()) scenes = entry.runtime_data.scenes
async_add_entities(SmartThingsScene(scene, client) for scene in scenes.values())
class SmartThingsScene(Scene): class SmartThingsScene(Scene):
"""Define a SmartThings scene.""" """Define a SmartThings scene."""
def __init__(self, scene): def __init__(self, scene: STScene, client: SmartThings) -> None:
"""Init the scene class.""" """Init the scene class."""
self.client = client
self._scene = scene self._scene = scene
self._attr_name = scene.name self._attr_name = scene.name
self._attr_unique_id = scene.scene_id self._attr_unique_id = scene.scene_id
async def async_activate(self, **kwargs: Any) -> None: async def async_activate(self, **kwargs: Any) -> None:
"""Activate scene.""" """Activate scene."""
await self._scene.execute() await self.client.execute_scene(self._scene.scene_id)
@property @property
def extra_state_attributes(self): def extra_state_attributes(self) -> dict[str, Any]:
"""Get attributes about the state.""" """Get attributes about the state."""
return { return {
"icon": self._scene.icon, "icon": self._scene.icon,

File diff suppressed because it is too large Load Diff

View File

@ -1,545 +0,0 @@
"""SmartApp functionality to receive cloud-push notifications."""
from __future__ import annotations
import asyncio
import functools
import logging
import secrets
from typing import Any
from urllib.parse import urlparse
from uuid import uuid4
from aiohttp import web
from pysmartapp import Dispatcher, SmartAppManager
from pysmartapp.const import SETTINGS_APP_ID
from pysmartthings import (
APP_TYPE_WEBHOOK,
CAPABILITIES,
CLASSIFICATION_AUTOMATION,
App,
AppEntity,
AppOAuth,
AppSettings,
InstalledAppStatus,
SmartThings,
SourceType,
Subscription,
SubscriptionEntity,
)
from homeassistant.components import cloud, webhook
from homeassistant.config_entries import ConfigFlowResult
from homeassistant.const import CONF_WEBHOOK_ID
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.network import NoURLAvailableError, get_url
from homeassistant.helpers.storage import Store
from .const import (
APP_NAME_PREFIX,
APP_OAUTH_CLIENT_NAME,
APP_OAUTH_SCOPES,
CONF_CLOUDHOOK_URL,
CONF_INSTALLED_APP_ID,
CONF_INSTANCE_ID,
CONF_REFRESH_TOKEN,
DATA_BROKERS,
DATA_MANAGER,
DOMAIN,
IGNORED_CAPABILITIES,
SETTINGS_INSTANCE_ID,
SIGNAL_SMARTAPP_PREFIX,
STORAGE_KEY,
STORAGE_VERSION,
SUBSCRIPTION_WARNING_LIMIT,
)
_LOGGER = logging.getLogger(__name__)
def format_unique_id(app_id: str, location_id: str) -> str:
"""Format the unique id for a config entry."""
return f"{app_id}_{location_id}"
async def find_app(hass: HomeAssistant, api: SmartThings) -> AppEntity | None:
"""Find an existing SmartApp for this installation of hass."""
apps = await api.apps()
for app in [app for app in apps if app.app_name.startswith(APP_NAME_PREFIX)]:
# Load settings to compare instance id
settings = await app.settings()
if (
settings.settings.get(SETTINGS_INSTANCE_ID)
== hass.data[DOMAIN][CONF_INSTANCE_ID]
):
return app
return None
async def validate_installed_app(api, installed_app_id: str):
"""Ensure the specified installed SmartApp is valid and functioning.
Query the API for the installed SmartApp and validate that it is tied to
the specified app_id and is in an authorized state.
"""
installed_app = await api.installed_app(installed_app_id)
if installed_app.installed_app_status != InstalledAppStatus.AUTHORIZED:
raise RuntimeWarning(
f"Installed SmartApp instance '{installed_app.display_name}' "
f"({installed_app.installed_app_id}) is not AUTHORIZED "
f"but instead {installed_app.installed_app_status}"
)
return installed_app
def validate_webhook_requirements(hass: HomeAssistant) -> bool:
"""Ensure Home Assistant is setup properly to receive webhooks."""
if cloud.async_active_subscription(hass):
return True
if hass.data[DOMAIN][CONF_CLOUDHOOK_URL] is not None:
return True
return get_webhook_url(hass).lower().startswith("https://")
def get_webhook_url(hass: HomeAssistant) -> str:
"""Get the URL of the webhook.
Return the cloudhook if available, otherwise local webhook.
"""
cloudhook_url = hass.data[DOMAIN][CONF_CLOUDHOOK_URL]
if cloud.async_active_subscription(hass) and cloudhook_url is not None:
return cloudhook_url
return webhook.async_generate_url(hass, hass.data[DOMAIN][CONF_WEBHOOK_ID])
def _get_app_template(hass: HomeAssistant):
try:
endpoint = f"at {get_url(hass, allow_cloud=False, prefer_external=True)}"
except NoURLAvailableError:
endpoint = ""
cloudhook_url = hass.data[DOMAIN][CONF_CLOUDHOOK_URL]
if cloudhook_url is not None:
endpoint = "via Nabu Casa"
description = f"{hass.config.location_name} {endpoint}"
return {
"app_name": APP_NAME_PREFIX + str(uuid4()),
"display_name": "Home Assistant",
"description": description,
"webhook_target_url": get_webhook_url(hass),
"app_type": APP_TYPE_WEBHOOK,
"single_instance": True,
"classifications": [CLASSIFICATION_AUTOMATION],
}
async def create_app(hass: HomeAssistant, api):
"""Create a SmartApp for this instance of hass."""
# Create app from template attributes
template = _get_app_template(hass)
app = App()
for key, value in template.items():
setattr(app, key, value)
app, client = await api.create_app(app)
_LOGGER.debug("Created SmartApp '%s' (%s)", app.app_name, app.app_id)
# Set unique hass id in settings
settings = AppSettings(app.app_id)
settings.settings[SETTINGS_APP_ID] = app.app_id
settings.settings[SETTINGS_INSTANCE_ID] = hass.data[DOMAIN][CONF_INSTANCE_ID]
await api.update_app_settings(settings)
_LOGGER.debug(
"Updated App Settings for SmartApp '%s' (%s)", app.app_name, app.app_id
)
# Set oauth scopes
oauth = AppOAuth(app.app_id)
oauth.client_name = APP_OAUTH_CLIENT_NAME
oauth.scope.extend(APP_OAUTH_SCOPES)
await api.update_app_oauth(oauth)
_LOGGER.debug("Updated App OAuth for SmartApp '%s' (%s)", app.app_name, app.app_id)
return app, client
async def update_app(hass: HomeAssistant, app):
"""Ensure the SmartApp is up-to-date and update if necessary."""
template = _get_app_template(hass)
template.pop("app_name") # don't update this
update_required = False
for key, value in template.items():
if getattr(app, key) != value:
update_required = True
setattr(app, key, value)
if update_required:
await app.save()
_LOGGER.debug(
"SmartApp '%s' (%s) updated with latest settings", app.app_name, app.app_id
)
def setup_smartapp(hass, app):
"""Configure an individual SmartApp in hass.
Register the SmartApp with the SmartAppManager so that hass will service
lifecycle events (install, event, etc...). A unique SmartApp is created
for each SmartThings account that is configured in hass.
"""
manager = hass.data[DOMAIN][DATA_MANAGER]
if smartapp := manager.smartapps.get(app.app_id):
# already setup
return smartapp
smartapp = manager.register(app.app_id, app.webhook_public_key)
smartapp.name = app.display_name
smartapp.description = app.description
smartapp.permissions.extend(APP_OAUTH_SCOPES)
return smartapp
async def setup_smartapp_endpoint(hass: HomeAssistant, fresh_install: bool):
"""Configure the SmartApp webhook in hass.
SmartApps are an extension point within the SmartThings ecosystem and
is used to receive push updates (i.e. device updates) from the cloud.
"""
if hass.data.get(DOMAIN):
# already setup
if not fresh_install:
return
# We're doing a fresh install, clean up
await unload_smartapp_endpoint(hass)
# Get/create config to store a unique id for this hass instance.
store = Store[dict[str, Any]](hass, STORAGE_VERSION, STORAGE_KEY)
if fresh_install or not (config := await store.async_load()):
# Create config
config = {
CONF_INSTANCE_ID: str(uuid4()),
CONF_WEBHOOK_ID: secrets.token_hex(),
CONF_CLOUDHOOK_URL: None,
}
await store.async_save(config)
# Register webhook
webhook.async_register(
hass, DOMAIN, "SmartApp", config[CONF_WEBHOOK_ID], smartapp_webhook
)
# Create webhook if eligible
cloudhook_url = config.get(CONF_CLOUDHOOK_URL)
if (
cloudhook_url is None
and cloud.async_active_subscription(hass)
and not hass.config_entries.async_entries(DOMAIN)
):
cloudhook_url = await cloud.async_create_cloudhook(
hass, config[CONF_WEBHOOK_ID]
)
config[CONF_CLOUDHOOK_URL] = cloudhook_url
await store.async_save(config)
_LOGGER.debug("Created cloudhook '%s'", cloudhook_url)
# SmartAppManager uses a dispatcher to invoke callbacks when push events
# occur. Use hass' implementation instead of the built-in one.
dispatcher = Dispatcher(
signal_prefix=SIGNAL_SMARTAPP_PREFIX,
connect=functools.partial(async_dispatcher_connect, hass),
send=functools.partial(async_dispatcher_send, hass),
)
# Path is used in digital signature validation
path = (
urlparse(cloudhook_url).path
if cloudhook_url
else webhook.async_generate_path(config[CONF_WEBHOOK_ID])
)
manager = SmartAppManager(path, dispatcher=dispatcher)
manager.connect_install(functools.partial(smartapp_install, hass))
manager.connect_update(functools.partial(smartapp_update, hass))
manager.connect_uninstall(functools.partial(smartapp_uninstall, hass))
hass.data[DOMAIN] = {
DATA_MANAGER: manager,
CONF_INSTANCE_ID: config[CONF_INSTANCE_ID],
DATA_BROKERS: {},
CONF_WEBHOOK_ID: config[CONF_WEBHOOK_ID],
# Will not be present if not enabled
CONF_CLOUDHOOK_URL: config.get(CONF_CLOUDHOOK_URL),
}
_LOGGER.debug(
"Setup endpoint for %s",
cloudhook_url
if cloudhook_url
else webhook.async_generate_url(hass, config[CONF_WEBHOOK_ID]),
)
async def unload_smartapp_endpoint(hass: HomeAssistant):
"""Tear down the component configuration."""
if DOMAIN not in hass.data:
return
# Remove the cloudhook if it was created
cloudhook_url = hass.data[DOMAIN][CONF_CLOUDHOOK_URL]
if cloudhook_url and cloud.async_is_logged_in(hass):
await cloud.async_delete_cloudhook(hass, hass.data[DOMAIN][CONF_WEBHOOK_ID])
# Remove cloudhook from storage
store = Store[dict[str, Any]](hass, STORAGE_VERSION, STORAGE_KEY)
await store.async_save(
{
CONF_INSTANCE_ID: hass.data[DOMAIN][CONF_INSTANCE_ID],
CONF_WEBHOOK_ID: hass.data[DOMAIN][CONF_WEBHOOK_ID],
CONF_CLOUDHOOK_URL: None,
}
)
_LOGGER.debug("Cloudhook '%s' was removed", cloudhook_url)
# Remove the webhook
webhook.async_unregister(hass, hass.data[DOMAIN][CONF_WEBHOOK_ID])
# Disconnect all brokers
for broker in hass.data[DOMAIN][DATA_BROKERS].values():
broker.disconnect()
# Remove all handlers from manager
hass.data[DOMAIN][DATA_MANAGER].dispatcher.disconnect_all()
# Remove the component data
hass.data.pop(DOMAIN)
async def smartapp_sync_subscriptions(
hass: HomeAssistant,
auth_token: str,
location_id: str,
installed_app_id: str,
devices,
):
"""Synchronize subscriptions of an installed up."""
api = SmartThings(async_get_clientsession(hass), auth_token)
tasks = []
async def create_subscription(target: str):
sub = Subscription()
sub.installed_app_id = installed_app_id
sub.location_id = location_id
sub.source_type = SourceType.CAPABILITY
sub.capability = target
try:
await api.create_subscription(sub)
_LOGGER.debug(
"Created subscription for '%s' under app '%s'", target, installed_app_id
)
except Exception as error: # noqa: BLE001
_LOGGER.error(
"Failed to create subscription for '%s' under app '%s': %s",
target,
installed_app_id,
error,
)
async def delete_subscription(sub: SubscriptionEntity):
try:
await api.delete_subscription(installed_app_id, sub.subscription_id)
_LOGGER.debug(
(
"Removed subscription for '%s' under app '%s' because it was no"
" longer needed"
),
sub.capability,
installed_app_id,
)
except Exception as error: # noqa: BLE001
_LOGGER.error(
"Failed to remove subscription for '%s' under app '%s': %s",
sub.capability,
installed_app_id,
error,
)
# Build set of capabilities and prune unsupported ones
capabilities = set()
for device in devices:
capabilities.update(device.capabilities)
# Remove items not defined in the library
capabilities.intersection_update(CAPABILITIES)
# Remove unused capabilities
capabilities.difference_update(IGNORED_CAPABILITIES)
capability_count = len(capabilities)
if capability_count > SUBSCRIPTION_WARNING_LIMIT:
_LOGGER.warning(
(
"Some device attributes may not receive push updates and there may be"
" subscription creation failures under app '%s' because %s"
" subscriptions are required but there is a limit of %s per app"
),
installed_app_id,
capability_count,
SUBSCRIPTION_WARNING_LIMIT,
)
_LOGGER.debug(
"Synchronizing subscriptions for %s capabilities under app '%s': %s",
capability_count,
installed_app_id,
capabilities,
)
# Get current subscriptions and find differences
subscriptions = await api.subscriptions(installed_app_id)
for subscription in subscriptions:
if subscription.capability in capabilities:
capabilities.remove(subscription.capability)
else:
# Delete the subscription
tasks.append(delete_subscription(subscription))
# Remaining capabilities need subscriptions created
tasks.extend([create_subscription(c) for c in capabilities])
if tasks:
await asyncio.gather(*tasks)
else:
_LOGGER.debug("Subscriptions for app '%s' are up-to-date", installed_app_id)
async def _find_and_continue_flow(
hass: HomeAssistant,
app_id: str,
location_id: str,
installed_app_id: str,
refresh_token: str,
):
"""Continue a config flow if one is in progress for the specific installed app."""
unique_id = format_unique_id(app_id, location_id)
flow = next(
(
flow
for flow in hass.config_entries.flow.async_progress_by_handler(DOMAIN)
if flow["context"].get("unique_id") == unique_id
),
None,
)
if flow is not None:
await _continue_flow(hass, app_id, installed_app_id, refresh_token, flow)
async def _continue_flow(
hass: HomeAssistant,
app_id: str,
installed_app_id: str,
refresh_token: str,
flow: ConfigFlowResult,
) -> None:
await hass.config_entries.flow.async_configure(
flow["flow_id"],
{
CONF_INSTALLED_APP_ID: installed_app_id,
CONF_REFRESH_TOKEN: refresh_token,
},
)
_LOGGER.debug(
"Continued config flow '%s' for SmartApp '%s' under parent app '%s'",
flow["flow_id"],
installed_app_id,
app_id,
)
async def smartapp_install(hass: HomeAssistant, req, resp, app):
"""Handle a SmartApp installation and continue the config flow."""
await _find_and_continue_flow(
hass, app.app_id, req.location_id, req.installed_app_id, req.refresh_token
)
_LOGGER.debug(
"Installed SmartApp '%s' under parent app '%s'",
req.installed_app_id,
app.app_id,
)
async def smartapp_update(hass: HomeAssistant, req, resp, app):
"""Handle a SmartApp update and either update the entry or continue the flow."""
unique_id = format_unique_id(app.app_id, req.location_id)
flow = next(
(
flow
for flow in hass.config_entries.flow.async_progress_by_handler(DOMAIN)
if flow["context"].get("unique_id") == unique_id
and flow["step_id"] == "authorize"
),
None,
)
if flow is not None:
await _continue_flow(
hass, app.app_id, req.installed_app_id, req.refresh_token, flow
)
_LOGGER.debug(
"Continued reauth flow '%s' for SmartApp '%s' under parent app '%s'",
flow["flow_id"],
req.installed_app_id,
app.app_id,
)
return
entry = next(
(
entry
for entry in hass.config_entries.async_entries(DOMAIN)
if entry.data.get(CONF_INSTALLED_APP_ID) == req.installed_app_id
),
None,
)
if entry:
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_REFRESH_TOKEN: req.refresh_token}
)
_LOGGER.debug(
"Updated config entry '%s' for SmartApp '%s' under parent app '%s'",
entry.entry_id,
req.installed_app_id,
app.app_id,
)
await _find_and_continue_flow(
hass, app.app_id, req.location_id, req.installed_app_id, req.refresh_token
)
_LOGGER.debug(
"Updated SmartApp '%s' under parent app '%s'", req.installed_app_id, app.app_id
)
async def smartapp_uninstall(hass: HomeAssistant, req, resp, app):
"""Handle when a SmartApp is removed from a location by the user.
Find and delete the config entry representing the integration.
"""
entry = next(
(
entry
for entry in hass.config_entries.async_entries(DOMAIN)
if entry.data.get(CONF_INSTALLED_APP_ID) == req.installed_app_id
),
None,
)
if entry:
# Add as job not needed because the current coroutine was invoked
# from the dispatcher and is not being awaited.
await hass.config_entries.async_remove(entry.entry_id)
_LOGGER.debug(
"Uninstalled SmartApp '%s' under parent app '%s'",
req.installed_app_id,
app.app_id,
)
async def smartapp_webhook(hass: HomeAssistant, webhook_id: str, request):
"""Handle a smartapp lifecycle event callback from SmartThings.
Requests from SmartThings are digitally signed and the SmartAppManager
validates the signature for authenticity.
"""
manager = hass.data[DOMAIN][DATA_MANAGER]
data = await request.json()
result = await manager.handle_request(data, request.headers)
return web.json_response(result)

View File

@ -1,43 +1,29 @@
{ {
"config": { "config": {
"step": { "step": {
"user": { "pick_implementation": {
"title": "Confirm Callback URL", "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
"description": "SmartThings will be configured to send push updates to Home Assistant at:\n> {webhook_url}\n\nIf this is not correct, please update your configuration, restart Home Assistant, and try again."
}, },
"pat": {
"title": "Enter Personal Access Token",
"description": "Please enter a SmartThings [Personal Access Token]({token_url}) that has been created per the [instructions]({component_url}). This will be used to create the Home Assistant integration within your SmartThings account.\n\n**Please note that all Personal Access Tokens created after 30 December 2024 are only valid for 24 hours, after which the integration will stop working. We are working on a fix.**",
"data": {
"access_token": "[%key:common::config_flow::data::access_token%]"
}
},
"select_location": {
"title": "Select Location",
"description": "Please select the SmartThings Location you wish to add to Home Assistant. We will then open a new window and ask you to login and authorize installation of the Home Assistant integration into the selected location.",
"data": { "location_id": "[%key:common::config_flow::data::location%]" }
},
"authorize": { "title": "Authorize Home Assistant" },
"reauth_confirm": { "reauth_confirm": {
"title": "Reauthorize Home Assistant", "title": "[%key:common::config_flow::title::reauth%]",
"description": "You are about to reauthorize Home Assistant with SmartThings. This will require you to log in and authorize the integration again." "description": "The SmartThings integration needs to re-authenticate your account"
},
"update_confirm": {
"title": "Finish reauthentication",
"description": "You have almost successfully reauthorized Home Assistant with SmartThings. Please press the button down below to finish the process."
} }
}, },
"abort": {
"invalid_webhook_url": "Home Assistant is not configured correctly to receive updates from SmartThings. The webhook URL is invalid:\n> {webhook_url}\n\nPlease update your configuration per the [instructions]({component_url}), restart Home Assistant, and try again.",
"no_available_locations": "There are no available SmartThings Locations to set up in Home Assistant.",
"reauth_successful": "Home Assistant has been successfully reauthorized with SmartThings."
},
"error": { "error": {
"token_invalid_format": "The token must be in the UID/GUID format", "already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
"token_unauthorized": "The token is invalid or no longer authorized.", },
"token_forbidden": "The token does not have the required OAuth scopes.", "abort": {
"app_setup_error": "Unable to set up the SmartApp. Please try again.", "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"webhook_error": "SmartThings could not validate the webhook URL. Please ensure the webhook URL is reachable from the internet and try again." "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reauth_account_mismatch": "Authenticated account does not match the account to be reauthenticated. Please log in with the correct account and pick the right location.",
"reauth_location_mismatch": "Authenticated location does not match the location to be reauthenticated. Please log in with the correct account and pick the right location."
} }
} }
} }

View File

@ -2,60 +2,67 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence
from typing import Any from typing import Any
from pysmartthings import Capability from pysmartthings import Attribute, Capability, Command
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DATA_BROKERS, DOMAIN from . import SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity from .entity import SmartThingsEntity
CAPABILITIES = (
Capability.SWITCH_LEVEL,
Capability.COLOR_CONTROL,
Capability.COLOR_TEMPERATURE,
Capability.FAN_SPEED,
)
AC_CAPABILITIES = (
Capability.AIR_CONDITIONER_MODE,
Capability.AIR_CONDITIONER_FAN_MODE,
Capability.TEMPERATURE_MEASUREMENT,
Capability.THERMOSTAT_COOLING_SETPOINT,
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, entry: SmartThingsConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Add switches for a config entry.""" """Add switches for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] entry_data = entry.runtime_data
async_add_entities( async_add_entities(
SmartThingsSwitch(device) SmartThingsSwitch(entry_data.client, device, {Capability.SWITCH})
for device in broker.devices.values() for device in entry_data.devices.values()
if broker.any_assigned(device.device_id, "switch") if Capability.SWITCH in device.status[MAIN]
and not any(capability in device.status[MAIN] for capability in CAPABILITIES)
and not all(capability in device.status[MAIN] for capability in AC_CAPABILITIES)
) )
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
"""Return all capabilities supported if minimum required are present."""
# Must be able to be turned on/off.
if Capability.switch in capabilities:
return [Capability.switch, Capability.energy_meter, Capability.power_meter]
return None
class SmartThingsSwitch(SmartThingsEntity, SwitchEntity): class SmartThingsSwitch(SmartThingsEntity, SwitchEntity):
"""Define a SmartThings switch.""" """Define a SmartThings switch."""
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off.""" """Turn the switch off."""
await self._device.switch_off(set_status=True) await self.execute_device_command(
# State is set optimistically in the command above, therefore update Capability.SWITCH,
# the entity state ahead of receiving the confirming push updates Command.OFF,
self.async_write_ha_state() )
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on.""" """Turn the switch on."""
await self._device.switch_on(set_status=True) await self.execute_device_command(
# State is set optimistically in the command above, therefore update Capability.SWITCH,
# the entity state ahead of receiving the confirming push updates Command.ON,
self.async_write_ha_state() )
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return true if light is on.""" """Return true if light is on."""
return self._device.status.switch return self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on"

View File

@ -28,6 +28,7 @@ APPLICATION_CREDENTIALS = [
"onedrive", "onedrive",
"point", "point",
"senz", "senz",
"smartthings",
"spotify", "spotify",
"tesla_fleet", "tesla_fleet",
"twitch", "twitch",

5
requirements_all.txt generated
View File

@ -2310,10 +2310,7 @@ pysma==0.7.5
pysmappee==0.2.29 pysmappee==0.2.29
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartapp==0.3.5 pysmartthings==1.2.0
# homeassistant.components.smartthings
pysmartthings==0.7.8
# homeassistant.components.smarty # homeassistant.components.smarty
pysmarty2==0.10.2 pysmarty2==0.10.2

View File

@ -1882,10 +1882,7 @@ pysma==0.7.5
pysmappee==0.2.29 pysmappee==0.2.29
# homeassistant.components.smartthings # homeassistant.components.smartthings
pysmartapp==0.3.5 pysmartthings==1.2.0
# homeassistant.components.smartthings
pysmartthings==0.7.8
# homeassistant.components.smarty # homeassistant.components.smarty
pysmarty2==0.10.2 pysmarty2==0.10.2

View File

@ -1 +1,75 @@
"""Tests for the SmartThings component.""" """Tests for the SmartThings integration."""
from typing import Any
from unittest.mock import AsyncMock
from pysmartthings.models import Attribute, Capability, DeviceEvent
from syrupy import SnapshotAssertion
from homeassistant.components.smartthings.const import MAIN
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Fixture for setting up the component."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
def snapshot_smartthings_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
platform: Platform,
) -> None:
"""Snapshot SmartThings entities."""
entities = hass.states.async_all(platform)
for entity_state in entities:
entity_entry = entity_registry.async_get(entity_state.entity_id)
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
assert entity_state == snapshot(name=f"{entity_entry.entity_id}-state")
def set_attribute_value(
mock: AsyncMock,
capability: Capability,
attribute: Attribute,
value: Any,
component: str = MAIN,
) -> None:
"""Set the value of an attribute."""
mock.get_device_status.return_value[component][capability][attribute].value = value
async def trigger_update(
hass: HomeAssistant,
mock: AsyncMock,
device_id: str,
capability: Capability,
attribute: Attribute,
value: str | float | dict[str, Any] | list[Any] | None,
data: dict[str, Any] | None = None,
) -> None:
"""Trigger an update."""
for call in mock.add_device_event_listener.call_args_list:
if call[0][0] == device_id and call[0][2] == capability:
call[0][3](
DeviceEvent(
"abc",
"abc",
"abc",
device_id,
MAIN,
capability,
attribute,
value,
data,
)
)
await hass.async_block_till_done()

View File

@ -1,358 +1,178 @@
"""Test configuration and mocks for the SmartThings component.""" """Test configuration and mocks for the SmartThings component."""
import secrets from collections.abc import Generator
from typing import Any import time
from unittest.mock import Mock, patch from unittest.mock import AsyncMock, patch
from uuid import uuid4
from pysmartthings import ( from pysmartthings.models import (
CLASSIFICATION_AUTOMATION, DeviceResponse,
AppEntity,
AppOAuthClient,
AppSettings,
DeviceEntity,
DeviceStatus, DeviceStatus,
InstalledApp, LocationResponse,
InstalledAppStatus, SceneResponse,
InstalledAppType,
Location,
SceneEntity,
SmartThings,
Subscription,
) )
from pysmartthings.api import Api
import pytest import pytest
from homeassistant.components import webhook from homeassistant.components.application_credentials import (
from homeassistant.components.smartthings import DeviceBroker ClientCredential,
async_import_client_credential,
)
from homeassistant.components.smartthings import CONF_INSTALLED_APP_ID
from homeassistant.components.smartthings.const import ( from homeassistant.components.smartthings.const import (
APP_NAME_PREFIX,
CONF_APP_ID,
CONF_INSTALLED_APP_ID,
CONF_INSTANCE_ID,
CONF_LOCATION_ID, CONF_LOCATION_ID,
CONF_REFRESH_TOKEN, CONF_REFRESH_TOKEN,
DATA_BROKERS,
DOMAIN, DOMAIN,
SETTINGS_INSTANCE_ID, SCOPES,
STORAGE_KEY,
STORAGE_VERSION,
)
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
from homeassistant.const import (
CONF_ACCESS_TOKEN,
CONF_CLIENT_ID,
CONF_CLIENT_SECRET,
CONF_WEBHOOK_ID,
) )
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.core_config import async_process_ha_core_config
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, load_fixture
from tests.components.light.conftest import mock_light_profiles # noqa: F401
COMPONENT_PREFIX = "homeassistant.components.smartthings."
async def setup_platform( @pytest.fixture
hass: HomeAssistant, platform: str, *, devices=None, scenes=None def mock_setup_entry() -> Generator[AsyncMock]:
): """Override async_setup_entry."""
"""Set up the SmartThings platform and prerequisites.""" with patch(
hass.config.components.add(DOMAIN) "homeassistant.components.smartthings.async_setup_entry",
config_entry = MockConfigEntry( return_value=True,
version=2, ) as mock_setup_entry:
domain=DOMAIN, yield mock_setup_entry
title="Test",
data={CONF_INSTALLED_APP_ID: str(uuid4())},
)
config_entry.add_to_hass(hass)
broker = DeviceBroker(
hass, config_entry, Mock(), Mock(), devices or [], scenes or []
)
hass.data[DOMAIN] = {DATA_BROKERS: {config_entry.entry_id: broker}}
config_entry.mock_state(hass, ConfigEntryState.LOADED) @pytest.fixture(name="expires_at")
await hass.config_entries.async_forward_entry_setups(config_entry, [platform]) def mock_expires_at() -> int:
await hass.async_block_till_done() """Fixture to set the oauth token expiration time."""
return config_entry return time.time() + 3600
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
async def setup_component( async def setup_credentials(hass: HomeAssistant) -> None:
hass: HomeAssistant, config_file: dict[str, str], hass_storage: dict[str, Any] """Fixture to setup credentials."""
) -> None: assert await async_setup_component(hass, "application_credentials", {})
"""Load the SmartThing component.""" await async_import_client_credential(
hass_storage[STORAGE_KEY] = {"data": config_file, "version": STORAGE_VERSION}
await async_process_ha_core_config(
hass, hass,
{"external_url": "https://test.local"}, DOMAIN,
) ClientCredential("CLIENT_ID", "CLIENT_SECRET"),
await async_setup_component(hass, "smartthings", {}) DOMAIN,
def _create_location() -> Mock:
loc = Mock(Location)
loc.name = "Test Location"
loc.location_id = str(uuid4())
return loc
@pytest.fixture(name="location")
def location_fixture() -> Mock:
"""Fixture for a single location."""
return _create_location()
@pytest.fixture(name="locations")
def locations_fixture(location: Mock) -> list[Mock]:
"""Fixture for 2 locations."""
return [location, _create_location()]
@pytest.fixture(name="app")
async def app_fixture(hass: HomeAssistant, config_file: dict[str, str]) -> Mock:
"""Fixture for a single app."""
app = Mock(AppEntity)
app.app_name = APP_NAME_PREFIX + str(uuid4())
app.app_id = str(uuid4())
app.app_type = "WEBHOOK_SMART_APP"
app.classifications = [CLASSIFICATION_AUTOMATION]
app.display_name = "Home Assistant"
app.description = f"{hass.config.location_name} at https://test.local"
app.single_instance = True
app.webhook_target_url = webhook.async_generate_url(
hass, hass.data[DOMAIN][CONF_WEBHOOK_ID]
) )
settings = Mock(AppSettings)
settings.app_id = app.app_id
settings.settings = {SETTINGS_INSTANCE_ID: config_file[CONF_INSTANCE_ID]}
app.settings.return_value = settings
return app
@pytest.fixture
@pytest.fixture(name="app_oauth_client") def mock_smartthings() -> Generator[AsyncMock]:
def app_oauth_client_fixture() -> Mock: """Mock a SmartThings client."""
"""Fixture for a single app's oauth."""
client = Mock(AppOAuthClient)
client.client_id = str(uuid4())
client.client_secret = str(uuid4())
return client
@pytest.fixture(name="app_settings")
def app_settings_fixture(app, config_file):
"""Fixture for an app settings."""
settings = Mock(AppSettings)
settings.app_id = app.app_id
settings.settings = {SETTINGS_INSTANCE_ID: config_file[CONF_INSTANCE_ID]}
return settings
def _create_installed_app(location_id: str, app_id: str) -> Mock:
item = Mock(InstalledApp)
item.installed_app_id = str(uuid4())
item.installed_app_status = InstalledAppStatus.AUTHORIZED
item.installed_app_type = InstalledAppType.WEBHOOK_SMART_APP
item.app_id = app_id
item.location_id = location_id
return item
@pytest.fixture(name="installed_app")
def installed_app_fixture(location: Mock, app: Mock) -> Mock:
"""Fixture for a single installed app."""
return _create_installed_app(location.location_id, app.app_id)
@pytest.fixture(name="installed_apps")
def installed_apps_fixture(installed_app, locations, app):
"""Fixture for 2 installed apps."""
return [installed_app, _create_installed_app(locations[1].location_id, app.app_id)]
@pytest.fixture(name="config_file")
def config_file_fixture() -> dict[str, str]:
"""Fixture representing the local config file contents."""
return {CONF_INSTANCE_ID: str(uuid4()), CONF_WEBHOOK_ID: secrets.token_hex()}
@pytest.fixture(name="smartthings_mock")
def smartthings_mock_fixture(locations):
"""Fixture to mock smartthings API calls."""
async def _location(location_id):
return next(
location for location in locations if location.location_id == location_id
)
smartthings_mock = Mock(SmartThings)
smartthings_mock.location.side_effect = _location
mock = Mock(return_value=smartthings_mock)
with ( with (
patch(COMPONENT_PREFIX + "SmartThings", new=mock), patch(
patch(COMPONENT_PREFIX + "config_flow.SmartThings", new=mock), "homeassistant.components.smartthings.SmartThings",
patch(COMPONENT_PREFIX + "smartapp.SmartThings", new=mock), autospec=True,
) as mock_client,
patch(
"homeassistant.components.smartthings.config_flow.SmartThings",
new=mock_client,
),
): ):
yield smartthings_mock client = mock_client.return_value
client.get_scenes.return_value = SceneResponse.from_json(
load_fixture("scenes.json", DOMAIN)
).items
client.get_locations.return_value = LocationResponse.from_json(
load_fixture("locations.json", DOMAIN)
).items
yield client
@pytest.fixture(name="device") @pytest.fixture(
def device_fixture(location): params=[
"""Fixture representing devices loaded.""" "da_ac_rac_000001",
item = Mock(DeviceEntity) "da_ac_rac_01001",
item.device_id = "743de49f-036f-4e9c-839a-2f89d57607db" "multipurpose_sensor",
item.name = "GE In-Wall Smart Dimmer" "contact_sensor",
item.label = "Front Porch Lights" "base_electric_meter",
item.location_id = location.location_id "smart_plug",
item.capabilities = [ "vd_stv_2017_k",
"switch", "c2c_arlo_pro_3_switch",
"switchLevel", "yale_push_button_deadbolt_lock",
"refresh", "ge_in_wall_smart_dimmer",
"indicator", "centralite",
"sensor", "da_ref_normal_000001",
"actuator", "vd_network_audio_002s",
"healthCheck", "iphone",
"light", "da_wm_dw_000001",
"da_wm_wd_000001",
"da_wm_wm_000001",
"da_rvc_normal_000001",
"da_ks_microwave_0101x",
"hue_color_temperature_bulb",
"hue_rgbw_color_bulb",
"c2c_shade",
"sonos_player",
"aeotec_home_energy_meter_gen5",
"virtual_water_sensor",
"virtual_thermostat",
"virtual_valve",
"sensibo_airconditioner_1",
"ecobee_sensor",
"ecobee_thermostat",
"fake_fan",
] ]
item.components = {"main": item.capabilities} )
item.status = Mock(DeviceStatus) def device_fixture(
return item mock_smartthings: AsyncMock, request: pytest.FixtureRequest
) -> Generator[str]:
"""Return every device."""
return request.param
@pytest.fixture(name="config_entry") @pytest.fixture
def config_entry_fixture(installed_app: Mock, location: Mock) -> MockConfigEntry: def devices(mock_smartthings: AsyncMock, device_fixture: str) -> Generator[AsyncMock]:
"""Fixture representing a config entry.""" """Return a specific device."""
data = { mock_smartthings.get_devices.return_value = DeviceResponse.from_json(
CONF_ACCESS_TOKEN: str(uuid4()), load_fixture(f"devices/{device_fixture}.json", DOMAIN)
CONF_INSTALLED_APP_ID: installed_app.installed_app_id, ).items
CONF_APP_ID: installed_app.app_id, mock_smartthings.get_device_status.return_value = DeviceStatus.from_json(
CONF_LOCATION_ID: location.location_id, load_fixture(f"device_status/{device_fixture}.json", DOMAIN)
CONF_REFRESH_TOKEN: str(uuid4()), ).components
CONF_CLIENT_ID: str(uuid4()), return mock_smartthings
CONF_CLIENT_SECRET: str(uuid4()),
}
@pytest.fixture
def mock_config_entry(expires_at: int) -> MockConfigEntry:
"""Mock a config entry."""
return MockConfigEntry( return MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
data=data, title="My home",
title=location.name, unique_id="397678e5-9995-4a39-9d9f-ae6ba310236c",
version=2, data={
source=SOURCE_USER, "auth_implementation": DOMAIN,
"token": {
"access_token": "mock-access-token",
"refresh_token": "mock-refresh-token",
"expires_at": expires_at,
"scope": " ".join(SCOPES),
"access_tier": 0,
"installed_app_id": "5aaaa925-2be1-4e40-b257-e4ef59083324",
},
CONF_LOCATION_ID: "397678e5-9995-4a39-9d9f-ae6ba310236c",
CONF_INSTALLED_APP_ID: "123",
},
version=3,
) )
@pytest.fixture(name="subscription_factory") @pytest.fixture
def subscription_factory_fixture(): def mock_old_config_entry() -> MockConfigEntry:
"""Fixture for creating mock subscriptions.""" """Mock the old config entry."""
return MockConfigEntry(
def _factory(capability): domain=DOMAIN,
sub = Subscription() title="My home",
sub.capability = capability unique_id="appid123-2be1-4e40-b257-e4ef59083324_397678e5-9995-4a39-9d9f-ae6ba310236c",
return sub data={
CONF_ACCESS_TOKEN: "mock-access-token",
return _factory CONF_REFRESH_TOKEN: "mock-refresh-token",
CONF_CLIENT_ID: "CLIENT_ID",
CONF_CLIENT_SECRET: "CLIENT_SECRET",
@pytest.fixture(name="device_factory") CONF_LOCATION_ID: "397678e5-9995-4a39-9d9f-ae6ba310236c",
def device_factory_fixture(): CONF_INSTALLED_APP_ID: "123aa123-2be1-4e40-b257-e4ef59083324",
"""Fixture for creating mock devices.""" },
api = Mock(Api) version=2,
api.post_device_command.return_value = {"results": [{"status": "ACCEPTED"}]} )
def _factory(label, capabilities, status: dict | None = None):
device_data = {
"deviceId": str(uuid4()),
"name": "Device Type Handler Name",
"label": label,
"deviceManufacturerCode": "9135fc86-0929-4436-bf73-5d75f523d9db",
"locationId": "fcd829e9-82f4-45b9-acfd-62fda029af80",
"components": [
{
"id": "main",
"capabilities": [
{"id": capability, "version": 1} for capability in capabilities
],
}
],
"dth": {
"deviceTypeId": "b678b29d-2726-4e4f-9c3f-7aa05bd08964",
"deviceTypeName": "Switch",
"deviceNetworkType": "ZWAVE",
},
"type": "DTH",
}
device = DeviceEntity(api, data=device_data)
if status:
for attribute, value in status.items():
device.status.apply_attribute_update("main", "", attribute, value)
return device
return _factory
@pytest.fixture(name="scene_factory")
def scene_factory_fixture(location):
"""Fixture for creating mock devices."""
def _factory(name):
scene = Mock(SceneEntity)
scene.scene_id = str(uuid4())
scene.name = name
scene.icon = None
scene.color = None
scene.location_id = location.location_id
return scene
return _factory
@pytest.fixture(name="scene")
def scene_fixture(scene_factory):
"""Fixture for an individual scene."""
return scene_factory("Test Scene")
@pytest.fixture(name="event_factory")
def event_factory_fixture():
"""Fixture for creating mock devices."""
def _factory(
device_id,
event_type="DEVICE_EVENT",
capability="",
attribute="Updated",
value="Value",
data=None,
):
event = Mock()
event.event_type = event_type
event.device_id = device_id
event.component_id = "main"
event.capability = capability
event.attribute = attribute
event.value = value
event.data = data
event.location_id = str(uuid4())
return event
return _factory
@pytest.fixture(name="event_request_factory")
def event_request_factory_fixture(event_factory):
"""Fixture for creating mock smartapp event requests."""
def _factory(device_ids=None, events=None):
request = Mock()
request.installed_app_id = uuid4()
if events is None:
events = []
if device_ids:
events.extend([event_factory(device_id) for device_id in device_ids])
events.append(event_factory(uuid4()))
events.append(event_factory(device_ids[0], event_type="OTHER"))
request.events = events
return request
return _factory

View File

@ -0,0 +1,31 @@
{
"components": {
"main": {
"powerMeter": {
"power": {
"value": 2859.743,
"unit": "W",
"timestamp": "2025-02-10T21:09:08.228Z"
}
},
"voltageMeasurement": {
"voltage": {
"value": null
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": null
}
},
"energyMeter": {
"energy": {
"value": 19978.536,
"unit": "kWh",
"timestamp": "2025-02-10T21:09:08.357Z"
}
},
"refresh": {}
}
}
}

View File

@ -0,0 +1,21 @@
{
"components": {
"main": {
"powerMeter": {
"power": {
"value": 938.3,
"unit": "W",
"timestamp": "2025-02-09T17:56:21.748Z"
}
},
"energyMeter": {
"energy": {
"value": 1930.362,
"unit": "kWh",
"timestamp": "2025-02-09T17:56:21.918Z"
}
},
"refresh": {}
}
}
}

View File

@ -0,0 +1,82 @@
{
"components": {
"main": {
"videoCapture": {
"stream": {
"value": null
},
"clip": {
"value": null
}
},
"videoStream": {
"supportedFeatures": {
"value": null
},
"stream": {
"value": null
}
},
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2025-02-03T21:55:57.991Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "online",
"data": {},
"timestamp": "2025-02-08T21:56:09.761Z"
}
},
"alarm": {
"alarm": {
"value": "off",
"timestamp": "2025-02-08T21:56:09.761Z"
}
},
"refresh": {},
"soundSensor": {
"sound": {
"value": "not detected",
"timestamp": "2025-02-08T21:56:09.761Z"
}
},
"motionSensor": {
"motion": {
"value": "inactive",
"timestamp": "2025-02-08T21:56:09.761Z"
}
},
"battery": {
"quantity": {
"value": null
},
"battery": {
"value": 100,
"unit": "%",
"timestamp": "2025-02-08T21:56:10.041Z"
},
"type": {
"value": null
}
},
"switch": {
"switch": {
"value": "on",
"timestamp": "2025-02-08T21:56:10.041Z"
}
}
}
}
}

View File

@ -0,0 +1,50 @@
{
"components": {
"main": {
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2025-02-07T23:01:15.966Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "offline",
"data": {
"reason": "DEVICE-OFFLINE"
},
"timestamp": "2025-02-08T09:04:47.694Z"
}
},
"switchLevel": {
"levelRange": {
"value": null
},
"level": {
"value": 100,
"unit": "%",
"timestamp": "2025-02-08T09:04:47.694Z"
}
},
"refresh": {},
"windowShade": {
"supportedWindowShadeCommands": {
"value": null
},
"windowShade": {
"value": "open",
"timestamp": "2025-02-08T09:04:47.694Z"
}
}
}
}
}

View File

@ -0,0 +1,60 @@
{
"components": {
"main": {
"powerMeter": {
"power": {
"value": 0.0,
"unit": "W",
"timestamp": "2025-02-09T17:49:15.190Z"
}
},
"switchLevel": {
"levelRange": {
"value": null
},
"level": {
"value": 0,
"unit": "%",
"timestamp": "2025-02-09T17:49:15.112Z"
}
},
"refresh": {},
"firmwareUpdate": {
"lastUpdateStatusReason": {
"value": null
},
"availableVersion": {
"value": "16015010",
"timestamp": "2025-01-26T10:19:54.783Z"
},
"lastUpdateStatus": {
"value": null
},
"supportedCommands": {
"value": null
},
"state": {
"value": "normalOperation",
"timestamp": "2025-01-26T10:19:54.788Z"
},
"updateAvailable": {
"value": false,
"timestamp": "2025-01-26T10:19:54.789Z"
},
"currentVersion": {
"value": "16015010",
"timestamp": "2025-01-26T10:19:54.775Z"
},
"lastUpdateTime": {
"value": null
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-09T17:24:16.864Z"
}
}
}
}
}

View File

@ -0,0 +1,66 @@
{
"components": {
"main": {
"contactSensor": {
"contact": {
"value": "closed",
"timestamp": "2025-02-09T17:16:42.674Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 59.0,
"unit": "F",
"timestamp": "2025-02-09T17:11:44.249Z"
}
},
"refresh": {},
"battery": {
"quantity": {
"value": null
},
"battery": {
"value": 100,
"unit": "%",
"timestamp": "2025-02-09T13:23:50.726Z"
},
"type": {
"value": null
}
},
"firmwareUpdate": {
"lastUpdateStatusReason": {
"value": null
},
"availableVersion": {
"value": "00000103",
"timestamp": "2025-02-09T13:59:19.101Z"
},
"lastUpdateStatus": {
"value": null
},
"supportedCommands": {
"value": null
},
"state": {
"value": "normalOperation",
"timestamp": "2025-02-09T13:59:19.101Z"
},
"updateAvailable": {
"value": false,
"timestamp": "2025-02-09T13:59:19.102Z"
},
"currentVersion": {
"value": "00000103",
"timestamp": "2025-02-09T13:59:19.102Z"
},
"lastUpdateTime": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,879 @@
{
"components": {
"1": {
"relativeHumidityMeasurement": {
"humidity": {
"value": 0,
"unit": "%",
"timestamp": "2021-04-06T16:43:35.291Z"
}
},
"custom.airConditionerOdorController": {
"airConditionerOdorControllerProgress": {
"value": null,
"timestamp": "2021-04-08T04:11:38.269Z"
},
"airConditionerOdorControllerState": {
"value": null,
"timestamp": "2021-04-08T04:11:38.269Z"
}
},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": null,
"timestamp": "2021-04-08T04:04:19.901Z"
},
"maximumSetpoint": {
"value": null,
"timestamp": "2021-04-08T04:04:19.901Z"
}
},
"airConditionerMode": {
"availableAcModes": {
"value": null
},
"supportedAcModes": {
"value": null,
"timestamp": "2021-04-08T03:50:50.930Z"
},
"airConditionerMode": {
"value": null,
"timestamp": "2021-04-08T03:50:50.930Z"
}
},
"custom.spiMode": {
"spiMode": {
"value": null,
"timestamp": "2021-04-06T16:57:57.686Z"
}
},
"airQualitySensor": {
"airQuality": {
"value": null,
"unit": "CAQI",
"timestamp": "2021-04-06T16:57:57.602Z"
}
},
"custom.airConditionerOptionalMode": {
"supportedAcOptionalMode": {
"value": null,
"timestamp": "2021-04-06T16:57:57.659Z"
},
"acOptionalMode": {
"value": null,
"timestamp": "2021-04-06T16:57:57.659Z"
}
},
"switch": {
"switch": {
"value": null,
"timestamp": "2021-04-06T16:44:10.518Z"
}
},
"custom.airConditionerTropicalNightMode": {
"acTropicalNightModeLevel": {
"value": null,
"timestamp": "2021-04-06T16:44:10.498Z"
}
},
"ocf": {
"st": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mndt": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnfv": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnhw": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"di": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnsl": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"dmv": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"n": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnmo": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"vid": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnmn": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnml": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnpv": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"mnos": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"pi": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
},
"icv": {
"value": null,
"timestamp": "2021-04-06T16:44:10.472Z"
}
},
"airConditionerFanMode": {
"fanMode": {
"value": null,
"timestamp": "2021-04-06T16:44:10.381Z"
},
"supportedAcFanModes": {
"value": ["auto", "low", "medium", "high", "turbo"],
"timestamp": "2024-09-10T10:26:28.605Z"
},
"availableAcFanModes": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"remoteControlStatus",
"airQualitySensor",
"dustSensor",
"odorSensor",
"veryFineDustSensor",
"custom.dustFilter",
"custom.deodorFilter",
"custom.deviceReportStateConfiguration",
"audioVolume",
"custom.autoCleaningMode",
"custom.airConditionerTropicalNightMode",
"custom.airConditionerOdorController",
"demandResponseLoadControl",
"relativeHumidityMeasurement"
],
"timestamp": "2024-09-10T10:26:28.605Z"
}
},
"fanOscillationMode": {
"supportedFanOscillationModes": {
"value": null,
"timestamp": "2021-04-06T16:44:10.325Z"
},
"availableFanOscillationModes": {
"value": null
},
"fanOscillationMode": {
"value": "fixed",
"timestamp": "2025-02-08T00:44:53.247Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": null,
"timestamp": "2021-04-06T16:44:10.373Z"
}
},
"dustSensor": {
"dustLevel": {
"value": null,
"unit": "\u03bcg/m^3",
"timestamp": "2021-04-06T16:44:10.122Z"
},
"fineDustLevel": {
"value": null,
"unit": "\u03bcg/m^3",
"timestamp": "2021-04-06T16:44:10.122Z"
}
},
"custom.deviceReportStateConfiguration": {
"reportStateRealtimePeriod": {
"value": null,
"timestamp": "2021-04-06T16:44:09.800Z"
},
"reportStateRealtime": {
"value": null,
"timestamp": "2021-04-06T16:44:09.800Z"
},
"reportStatePeriod": {
"value": null,
"timestamp": "2021-04-06T16:44:09.800Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": null,
"timestamp": "2021-04-06T16:43:59.136Z"
}
},
"demandResponseLoadControl": {
"drlcStatus": {
"value": null,
"timestamp": "2021-04-06T16:43:54.748Z"
}
},
"audioVolume": {
"volume": {
"value": null,
"unit": "%",
"timestamp": "2021-04-06T16:43:53.541Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": null,
"timestamp": "2021-04-06T16:43:53.364Z"
}
},
"custom.autoCleaningMode": {
"supportedAutoCleaningModes": {
"value": null
},
"timedCleanDuration": {
"value": null
},
"operatingState": {
"value": null
},
"timedCleanDurationRange": {
"value": null
},
"supportedOperatingStates": {
"value": null
},
"progress": {
"value": null
},
"autoCleaningMode": {
"value": null,
"timestamp": "2021-04-06T16:43:53.344Z"
}
},
"custom.dustFilter": {
"dustFilterUsageStep": {
"value": null,
"timestamp": "2021-04-06T16:43:39.145Z"
},
"dustFilterUsage": {
"value": null,
"timestamp": "2021-04-06T16:43:39.145Z"
},
"dustFilterLastResetDate": {
"value": null,
"timestamp": "2021-04-06T16:43:39.145Z"
},
"dustFilterStatus": {
"value": null,
"timestamp": "2021-04-06T16:43:39.145Z"
},
"dustFilterCapacity": {
"value": null,
"timestamp": "2021-04-06T16:43:39.145Z"
},
"dustFilterResetType": {
"value": null,
"timestamp": "2021-04-06T16:43:39.145Z"
}
},
"odorSensor": {
"odorLevel": {
"value": null,
"timestamp": "2021-04-06T16:43:38.992Z"
}
},
"remoteControlStatus": {
"remoteControlEnabled": {
"value": null,
"timestamp": "2021-04-06T16:43:39.097Z"
}
},
"custom.deodorFilter": {
"deodorFilterCapacity": {
"value": null,
"timestamp": "2021-04-06T16:43:39.118Z"
},
"deodorFilterLastResetDate": {
"value": null,
"timestamp": "2021-04-06T16:43:39.118Z"
},
"deodorFilterStatus": {
"value": null,
"timestamp": "2021-04-06T16:43:39.118Z"
},
"deodorFilterResetType": {
"value": null,
"timestamp": "2021-04-06T16:43:39.118Z"
},
"deodorFilterUsage": {
"value": null,
"timestamp": "2021-04-06T16:43:39.118Z"
},
"deodorFilterUsageStep": {
"value": null,
"timestamp": "2021-04-06T16:43:39.118Z"
}
},
"custom.energyType": {
"energyType": {
"value": null,
"timestamp": "2021-04-06T16:43:38.843Z"
},
"energySavingSupport": {
"value": null
},
"drMaxDuration": {
"value": null
},
"energySavingLevel": {
"value": null
},
"energySavingInfo": {
"value": null
},
"supportedEnergySavingLevels": {
"value": null
},
"energySavingOperation": {
"value": null
},
"notificationTemplateID": {
"value": null
},
"energySavingOperationSupport": {
"value": null
}
},
"veryFineDustSensor": {
"veryFineDustLevel": {
"value": null,
"unit": "\u03bcg/m^3",
"timestamp": "2021-04-06T16:43:38.529Z"
}
}
},
"main": {
"relativeHumidityMeasurement": {
"humidity": {
"value": 60,
"unit": "%",
"timestamp": "2024-12-30T13:10:23.759Z"
}
},
"custom.airConditionerOdorController": {
"airConditionerOdorControllerProgress": {
"value": null,
"timestamp": "2021-04-06T16:43:37.555Z"
},
"airConditionerOdorControllerState": {
"value": null,
"timestamp": "2021-04-06T16:43:37.555Z"
}
},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": 16,
"unit": "C",
"timestamp": "2025-01-08T06:30:58.307Z"
},
"maximumSetpoint": {
"value": 30,
"unit": "C",
"timestamp": "2024-09-10T10:26:28.781Z"
}
},
"airConditionerMode": {
"availableAcModes": {
"value": null
},
"supportedAcModes": {
"value": ["cool", "dry", "wind", "auto", "heat"],
"timestamp": "2024-09-10T10:26:28.781Z"
},
"airConditionerMode": {
"value": "heat",
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"custom.spiMode": {
"spiMode": {
"value": "off",
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"samsungce.dongleSoftwareInstallation": {
"status": {
"value": "completed",
"timestamp": "2021-12-29T01:36:51.289Z"
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": null
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": null
},
"description": {
"value": null
},
"releaseYear": {
"value": null
},
"binaryId": {
"value": "ARTIK051_KRAC_18K",
"timestamp": "2025-02-08T00:44:53.855Z"
}
},
"airQualitySensor": {
"airQuality": {
"value": null,
"unit": "CAQI",
"timestamp": "2021-04-06T16:43:37.208Z"
}
},
"custom.airConditionerOptionalMode": {
"supportedAcOptionalMode": {
"value": ["off", "windFree"],
"timestamp": "2024-09-10T10:26:28.781Z"
},
"acOptionalMode": {
"value": "off",
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-09T16:37:54.072Z"
}
},
"custom.airConditionerTropicalNightMode": {
"acTropicalNightModeLevel": {
"value": 0,
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"ocf": {
"st": {
"value": null,
"timestamp": "2021-04-06T16:43:35.933Z"
},
"mndt": {
"value": null,
"timestamp": "2021-04-06T16:43:35.912Z"
},
"mnfv": {
"value": "0.1.0",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnhw": {
"value": "1.0",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"di": {
"value": "96a5ef74-5832-a84b-f1f7-ca799957065d",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnsl": {
"value": null,
"timestamp": "2021-04-06T16:43:35.803Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"n": {
"value": "[room a/c] Samsung",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnmo": {
"value": "ARTIK051_KRAC_18K|10193441|60010132001111110200000000000000",
"timestamp": "2024-09-10T10:26:28.781Z"
},
"vid": {
"value": "DA-AC-RAC-000001",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnpv": {
"value": "0G3MPDCKA00010E",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"mnos": {
"value": "TizenRT2.0",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"pi": {
"value": "96a5ef74-5832-a84b-f1f7-ca799957065d",
"timestamp": "2024-09-10T10:26:28.552Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2024-09-10T10:26:28.552Z"
}
},
"airConditionerFanMode": {
"fanMode": {
"value": "low",
"timestamp": "2025-02-09T09:14:39.249Z"
},
"supportedAcFanModes": {
"value": ["auto", "low", "medium", "high", "turbo"],
"timestamp": "2025-02-09T09:14:39.249Z"
},
"availableAcFanModes": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"remoteControlStatus",
"airQualitySensor",
"dustSensor",
"veryFineDustSensor",
"custom.dustFilter",
"custom.deodorFilter",
"custom.deviceReportStateConfiguration",
"samsungce.dongleSoftwareInstallation",
"demandResponseLoadControl",
"custom.airConditionerOdorController"
],
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 24070101,
"timestamp": "2024-09-04T06:35:09.557Z"
}
},
"fanOscillationMode": {
"supportedFanOscillationModes": {
"value": null,
"timestamp": "2021-04-06T16:43:35.782Z"
},
"availableFanOscillationModes": {
"value": null
},
"fanOscillationMode": {
"value": "fixed",
"timestamp": "2025-02-09T09:14:39.249Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 25,
"unit": "C",
"timestamp": "2025-02-09T16:33:29.164Z"
}
},
"dustSensor": {
"dustLevel": {
"value": null,
"unit": "\u03bcg/m^3",
"timestamp": "2021-04-06T16:43:35.665Z"
},
"fineDustLevel": {
"value": null,
"unit": "\u03bcg/m^3",
"timestamp": "2021-04-06T16:43:35.665Z"
}
},
"custom.deviceReportStateConfiguration": {
"reportStateRealtimePeriod": {
"value": null,
"timestamp": "2021-04-06T16:43:35.643Z"
},
"reportStateRealtime": {
"value": null,
"timestamp": "2021-04-06T16:43:35.643Z"
},
"reportStatePeriod": {
"value": null,
"timestamp": "2021-04-06T16:43:35.643Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": 25,
"unit": "C",
"timestamp": "2025-02-09T09:15:11.608Z"
}
},
"custom.disabledComponents": {
"disabledComponents": {
"value": ["1"],
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"demandResponseLoadControl": {
"drlcStatus": {
"value": {
"drlcType": 1,
"drlcLevel": -1,
"start": "1970-01-01T00:00:00Z",
"duration": 0,
"override": false
},
"timestamp": "2024-09-10T10:26:28.781Z"
}
},
"audioVolume": {
"volume": {
"value": 100,
"unit": "%",
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": {
"energy": 2247300,
"deltaEnergy": 400,
"power": 0,
"powerEnergy": 0.0,
"persistedEnergy": 2247300,
"energySaved": 0,
"start": "2025-02-09T15:45:29Z",
"end": "2025-02-09T16:15:33Z"
},
"timestamp": "2025-02-09T16:15:33.639Z"
}
},
"custom.autoCleaningMode": {
"supportedAutoCleaningModes": {
"value": null
},
"timedCleanDuration": {
"value": null
},
"operatingState": {
"value": null
},
"timedCleanDurationRange": {
"value": null
},
"supportedOperatingStates": {
"value": null
},
"progress": {
"value": null
},
"autoCleaningMode": {
"value": "off",
"timestamp": "2025-02-09T09:14:39.642Z"
}
},
"refresh": {},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["oic.r.temperature"],
"if": ["oic.if.baseline", "oic.if.a"],
"range": [16.0, 30.0],
"units": "C",
"temperature": 22.0
}
},
"data": {
"href": "/temperature/desired/0"
},
"timestamp": "2023-07-19T03:07:43.270Z"
}
},
"samsungce.selfCheck": {
"result": {
"value": null
},
"supportedActions": {
"value": ["start"],
"timestamp": "2024-09-04T06:35:09.557Z"
},
"progress": {
"value": null
},
"errors": {
"value": [],
"timestamp": "2025-02-08T00:44:53.349Z"
},
"status": {
"value": "ready",
"timestamp": "2025-02-08T00:44:53.549Z"
}
},
"custom.dustFilter": {
"dustFilterUsageStep": {
"value": null,
"timestamp": "2021-04-06T16:43:35.527Z"
},
"dustFilterUsage": {
"value": null,
"timestamp": "2021-04-06T16:43:35.527Z"
},
"dustFilterLastResetDate": {
"value": null,
"timestamp": "2021-04-06T16:43:35.527Z"
},
"dustFilterStatus": {
"value": null,
"timestamp": "2021-04-06T16:43:35.527Z"
},
"dustFilterCapacity": {
"value": null,
"timestamp": "2021-04-06T16:43:35.527Z"
},
"dustFilterResetType": {
"value": null,
"timestamp": "2021-04-06T16:43:35.527Z"
}
},
"remoteControlStatus": {
"remoteControlEnabled": {
"value": null,
"timestamp": "2021-04-06T16:43:35.379Z"
}
},
"custom.deodorFilter": {
"deodorFilterCapacity": {
"value": null,
"timestamp": "2021-04-06T16:43:35.502Z"
},
"deodorFilterLastResetDate": {
"value": null,
"timestamp": "2021-04-06T16:43:35.502Z"
},
"deodorFilterStatus": {
"value": null,
"timestamp": "2021-04-06T16:43:35.502Z"
},
"deodorFilterResetType": {
"value": null,
"timestamp": "2021-04-06T16:43:35.502Z"
},
"deodorFilterUsage": {
"value": null,
"timestamp": "2021-04-06T16:43:35.502Z"
},
"deodorFilterUsageStep": {
"value": null,
"timestamp": "2021-04-06T16:43:35.502Z"
}
},
"custom.energyType": {
"energyType": {
"value": "1.0",
"timestamp": "2024-09-10T10:26:28.781Z"
},
"energySavingSupport": {
"value": false,
"timestamp": "2021-12-29T07:29:17.526Z"
},
"drMaxDuration": {
"value": null
},
"energySavingLevel": {
"value": null
},
"energySavingInfo": {
"value": null
},
"supportedEnergySavingLevels": {
"value": null
},
"energySavingOperation": {
"value": null
},
"notificationTemplateID": {
"value": null
},
"energySavingOperationSupport": {
"value": null
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": null
},
"otnDUID": {
"value": "43CEZFTFFL7Z2",
"timestamp": "2025-02-08T00:44:53.855Z"
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": [],
"timestamp": "2025-02-08T00:44:53.855Z"
},
"newVersionAvailable": {
"value": false,
"timestamp": "2025-02-08T00:44:53.855Z"
},
"operatingState": {
"value": null
},
"progress": {
"value": null
}
},
"veryFineDustSensor": {
"veryFineDustLevel": {
"value": null,
"unit": "\u03bcg/m^3",
"timestamp": "2021-04-06T16:43:35.363Z"
}
}
}
}
}

View File

@ -0,0 +1,731 @@
{
"components": {
"main": {
"samsungce.unavailableCapabilities": {
"unavailableCommands": {
"value": ["custom.spiMode.setSpiMode"],
"timestamp": "2025-02-09T05:44:01.769Z"
}
},
"relativeHumidityMeasurement": {
"humidity": {
"value": 42,
"unit": "%",
"timestamp": "2025-02-09T17:02:45.042Z"
}
},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": 16,
"unit": "C",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"maximumSetpoint": {
"value": 30,
"unit": "C",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"airConditionerMode": {
"availableAcModes": {
"value": [],
"timestamp": "2025-02-09T14:35:56.800Z"
},
"supportedAcModes": {
"value": ["auto", "cool", "dry", "wind", "heat"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"airConditionerMode": {
"value": "cool",
"timestamp": "2025-02-09T04:52:00.923Z"
}
},
"custom.spiMode": {
"spiMode": {
"value": null
}
},
"custom.airConditionerOptionalMode": {
"supportedAcOptionalMode": {
"value": [
"off",
"sleep",
"quiet",
"smart",
"speed",
"windFree",
"windFreeSleep"
],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"acOptionalMode": {
"value": "off",
"timestamp": "2025-02-09T05:44:01.853Z"
}
},
"samsungce.airConditionerBeep": {
"beep": {
"value": "off",
"timestamp": "2025-02-09T04:52:00.923Z"
}
},
"ocf": {
"st": {
"value": null
},
"mndt": {
"value": null
},
"mnfv": {
"value": "ARA-WW-TP1-22-COMMON_11240702",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"mnhw": {
"value": "Realtek",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"di": {
"value": "4ece486b-89db-f06a-d54d-748b676b4d8e",
"timestamp": "2025-02-09T15:42:12.714Z"
},
"mnsl": {
"value": "http://www.samsung.com",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2025-02-09T15:42:12.714Z"
},
"n": {
"value": "Samsung-Room-Air-Conditioner",
"timestamp": "2025-02-09T15:42:12.714Z"
},
"mnmo": {
"value": "ARA-WW-TP1-22-COMMON|10229641|60010523001511014600083200800000",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"vid": {
"value": "DA-AC-RAC-01001",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"mnpv": {
"value": "DAWIT 2.0",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"mnos": {
"value": "TizenRT 3.1",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"pi": {
"value": "4ece486b-89db-f06a-d54d-748b676b4d8e",
"timestamp": "2025-02-09T15:42:12.723Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2025-02-09T15:42:12.714Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"custom.deodorFilter",
"custom.electricHepaFilter",
"custom.periodicSensing",
"custom.doNotDisturbMode",
"samsungce.deviceInfoPrivate",
"samsungce.quickControl",
"samsungce.welcomeCooling",
"samsungce.airConditionerBeep",
"samsungce.airConditionerLighting",
"samsungce.individualControlLock",
"samsungce.alwaysOnSensing",
"samsungce.buttonDisplayCondition",
"airQualitySensor",
"dustSensor",
"odorSensor",
"veryFineDustSensor",
"custom.spiMode",
"audioNotification"
],
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 24100102,
"timestamp": "2025-01-28T21:31:35.935Z"
}
},
"sec.diagnosticsInformation": {
"logType": {
"value": ["errCode", "dump"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"endpoint": {
"value": "SSM",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"minVersion": {
"value": "1.0",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"signinPermission": {
"value": null
},
"setupId": {
"value": "010",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"protocolType": {
"value": "wifi_https",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"tsId": {
"value": "DA01",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"mnId": {
"value": "0AJT",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"dumpType": {
"value": "file",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"fanOscillationMode": {
"supportedFanOscillationModes": {
"value": ["fixed", "vertical", "horizontal", "all"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"availableFanOscillationModes": {
"value": null
},
"fanOscillationMode": {
"value": "fixed",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"custom.periodicSensing": {
"automaticExecutionSetting": {
"value": null
},
"automaticExecutionMode": {
"value": null
},
"supportedAutomaticExecutionSetting": {
"value": null
},
"supportedAutomaticExecutionMode": {
"value": null
},
"periodicSensing": {
"value": null
},
"periodicSensingInterval": {
"value": null
},
"lastSensingTime": {
"value": null
},
"lastSensingLevel": {
"value": null
},
"periodicSensingStatus": {
"value": null
}
},
"demandResponseLoadControl": {
"drlcStatus": {
"value": {
"drlcType": 1,
"drlcLevel": 0,
"start": "1970-01-01T00:00:00Z",
"duration": 0,
"override": false
},
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"audioVolume": {
"volume": {
"value": 0,
"unit": "%",
"timestamp": "2025-02-09T04:52:00.923Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": {
"energy": 13836,
"deltaEnergy": 0,
"power": 0,
"powerEnergy": 0.0,
"persistedEnergy": 13836,
"energySaved": 0,
"persistedSavedEnergy": 0,
"start": "2025-02-09T16:08:15Z",
"end": "2025-02-09T17:02:44Z"
},
"timestamp": "2025-02-09T17:02:44.883Z"
}
},
"custom.autoCleaningMode": {
"supportedAutoCleaningModes": {
"value": null
},
"timedCleanDuration": {
"value": null
},
"operatingState": {
"value": null
},
"timedCleanDurationRange": {
"value": null
},
"supportedOperatingStates": {
"value": null
},
"progress": {
"value": null
},
"autoCleaningMode": {
"value": "on",
"timestamp": "2025-02-09T05:44:02.014Z"
}
},
"samsungce.individualControlLock": {
"lockState": {
"value": null
}
},
"audioNotification": {},
"execute": {
"data": {
"value": null
}
},
"samsungce.welcomeCooling": {
"latestRequestId": {
"value": null
},
"operatingState": {
"value": null
}
},
"sec.wifiConfiguration": {
"autoReconnection": {
"value": true,
"timestamp": "2025-02-09T15:42:13.444Z"
},
"minVersion": {
"value": "1.0",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"supportedWiFiFreq": {
"value": ["2.4G"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"supportedAuthType": {
"value": ["OPEN", "WEP", "WPA-PSK", "WPA2-PSK", "SAE"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"protocolType": {
"value": ["helper_hotspot", "ble_ocf"],
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"samsungce.selfCheck": {
"result": {
"value": null
},
"supportedActions": {
"value": ["start", "cancel"],
"timestamp": "2025-02-09T04:52:00.923Z"
},
"progress": {
"value": 1,
"unit": "%",
"timestamp": "2025-02-09T04:52:00.923Z"
},
"errors": {
"value": [],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"status": {
"value": "ready",
"timestamp": "2025-02-09T04:52:00.923Z"
}
},
"custom.dustFilter": {
"dustFilterUsageStep": {
"value": 1,
"timestamp": "2025-02-09T12:00:10.310Z"
},
"dustFilterUsage": {
"value": 12,
"timestamp": "2025-02-09T12:00:10.310Z"
},
"dustFilterLastResetDate": {
"value": null
},
"dustFilterStatus": {
"value": "normal",
"timestamp": "2025-02-09T12:00:10.310Z"
},
"dustFilterCapacity": {
"value": 500,
"unit": "Hour",
"timestamp": "2025-02-09T12:00:10.310Z"
},
"dustFilterResetType": {
"value": ["replaceable", "washable"],
"timestamp": "2025-02-09T12:00:10.310Z"
}
},
"custom.energyType": {
"energyType": {
"value": "1.0",
"timestamp": "2025-01-28T21:31:39.517Z"
},
"energySavingSupport": {
"value": true,
"timestamp": "2025-01-28T21:38:35.560Z"
},
"drMaxDuration": {
"value": 99999999,
"unit": "min",
"timestamp": "2025-01-28T21:31:37.357Z"
},
"energySavingLevel": {
"value": null
},
"energySavingInfo": {
"value": null
},
"supportedEnergySavingLevels": {
"value": null
},
"energySavingOperation": {
"value": false,
"timestamp": "2025-02-09T15:42:13.444Z"
},
"notificationTemplateID": {
"value": null
},
"energySavingOperationSupport": {
"value": true,
"timestamp": "2025-01-28T21:38:35.731Z"
}
},
"bypassable": {
"bypassStatus": {
"value": "bypassed",
"timestamp": "2025-01-28T21:31:35.935Z"
}
},
"samsungce.airQualityHealthConcern": {
"supportedAirQualityHealthConcerns": {
"value": null
},
"airQualityHealthConcern": {
"value": null
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": {},
"timestamp": "2025-02-05T20:07:11.459Z"
},
"otnDUID": {
"value": "U7CB2ZD4QPDUC",
"timestamp": "2025-02-09T15:42:13.444Z"
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": [],
"timestamp": "2025-01-28T21:31:38.089Z"
},
"newVersionAvailable": {
"value": false,
"timestamp": "2025-02-09T15:42:13.444Z"
},
"operatingState": {
"value": "none",
"timestamp": "2025-02-05T20:07:11.459Z"
},
"progress": {
"value": null
}
},
"veryFineDustSensor": {
"veryFineDustLevel": {
"value": null
}
},
"custom.veryFineDustFilter": {
"veryFineDustFilterStatus": {
"value": null
},
"veryFineDustFilterResetType": {
"value": null
},
"veryFineDustFilterUsage": {
"value": null
},
"veryFineDustFilterLastResetDate": {
"value": null
},
"veryFineDustFilterUsageStep": {
"value": null
},
"veryFineDustFilterCapacity": {
"value": null
}
},
"samsungce.silentAction": {},
"custom.airConditionerOdorController": {
"airConditionerOdorControllerProgress": {
"value": 0,
"timestamp": "2025-02-09T04:52:00.923Z"
},
"airConditionerOdorControllerState": {
"value": "off",
"timestamp": "2025-02-09T04:52:00.923Z"
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": null
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": null
},
"description": {
"value": null
},
"releaseYear": {
"value": 21,
"timestamp": "2025-01-28T21:31:35.935Z"
},
"binaryId": {
"value": "ARA-WW-TP1-22-COMMON",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"airQualitySensor": {
"airQuality": {
"value": null
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"samsungce.quickControl": {
"version": {
"value": null
}
},
"custom.airConditionerTropicalNightMode": {
"acTropicalNightModeLevel": {
"value": 6,
"timestamp": "2025-02-09T04:52:00.923Z"
}
},
"airConditionerFanMode": {
"fanMode": {
"value": "high",
"timestamp": "2025-02-09T14:07:45.816Z"
},
"supportedAcFanModes": {
"value": ["auto", "low", "medium", "high", "turbo"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"availableAcFanModes": {
"value": ["auto", "low", "medium", "high", "turbo"],
"timestamp": "2025-02-09T05:44:01.769Z"
}
},
"samsungce.dustFilterAlarm": {
"alarmThreshold": {
"value": 500,
"unit": "Hour",
"timestamp": "2025-02-09T12:00:10.310Z"
},
"supportedAlarmThresholds": {
"value": [180, 300, 500, 700],
"unit": "Hour",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"custom.electricHepaFilter": {
"electricHepaFilterCapacity": {
"value": null
},
"electricHepaFilterUsageStep": {
"value": null
},
"electricHepaFilterLastResetDate": {
"value": null
},
"electricHepaFilterStatus": {
"value": null
},
"electricHepaFilterUsage": {
"value": null
},
"electricHepaFilterResetType": {
"value": null
}
},
"samsungce.airConditionerLighting": {
"supportedLightingLevels": {
"value": ["on", "off"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"lighting": {
"value": "on",
"timestamp": "2025-02-09T09:30:03.213Z"
}
},
"samsungce.buttonDisplayCondition": {
"switch": {
"value": "enabled",
"timestamp": "2025-02-09T05:17:41.282Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 27,
"unit": "C",
"timestamp": "2025-02-09T16:38:17.028Z"
}
},
"dustSensor": {
"dustLevel": {
"value": null
},
"fineDustLevel": {
"value": null
}
},
"sec.calmConnectionCare": {
"role": {
"value": ["things"],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"protocols": {
"value": null
},
"version": {
"value": "1.0",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"custom.deviceReportStateConfiguration": {
"reportStateRealtimePeriod": {
"value": "disabled",
"timestamp": "2025-02-09T05:17:39.792Z"
},
"reportStateRealtime": {
"value": {
"state": "disabled"
},
"timestamp": "2025-02-09T15:42:13.444Z"
},
"reportStatePeriod": {
"value": "enabled",
"timestamp": "2025-02-09T05:17:39.792Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": {
"minimum": 16,
"maximum": 30,
"step": 1
},
"unit": "C",
"timestamp": "2025-02-09T05:17:41.533Z"
},
"coolingSetpoint": {
"value": 23,
"unit": "C",
"timestamp": "2025-02-09T14:07:45.643Z"
}
},
"samsungce.alwaysOnSensing": {
"origins": {
"value": [],
"timestamp": "2025-02-09T15:42:13.444Z"
},
"alwaysOn": {
"value": "off",
"timestamp": "2025-02-09T15:42:13.444Z"
}
},
"refresh": {},
"odorSensor": {
"odorLevel": {
"value": null
}
},
"custom.deodorFilter": {
"deodorFilterCapacity": {
"value": null
},
"deodorFilterLastResetDate": {
"value": null
},
"deodorFilterStatus": {
"value": null
},
"deodorFilterResetType": {
"value": null
},
"deodorFilterUsage": {
"value": null
},
"deodorFilterUsageStep": {
"value": null
}
},
"custom.doNotDisturbMode": {
"doNotDisturb": {
"value": null
},
"startTime": {
"value": null
},
"endTime": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,600 @@
{
"components": {
"main": {
"doorControl": {
"door": {
"value": null
}
},
"samsungce.kitchenDeviceDefaults": {
"defaultOperationTime": {
"value": 30,
"timestamp": "2022-03-23T15:59:12.609Z"
},
"defaultOvenMode": {
"value": "MicroWave",
"timestamp": "2025-02-08T21:13:36.289Z"
},
"defaultOvenSetpoint": {
"value": null
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": null
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": null
},
"description": {
"value": null
},
"releaseYear": {
"value": null
},
"binaryId": {
"value": "TP2X_DA-KS-MICROWAVE-0101X",
"timestamp": "2025-02-08T21:13:36.256Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-09T00:11:12.010Z"
}
},
"ocf": {
"st": {
"value": null
},
"mndt": {
"value": null
},
"mnfv": {
"value": "AKS-WW-TP2-20-MICROWAVE-OTR_40230125",
"timestamp": "2023-07-03T06:44:54.757Z"
},
"mnhw": {
"value": "MediaTek",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"di": {
"value": "2bad3237-4886-e699-1b90-4a51a3d55c8a",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"mnsl": {
"value": "http://www.samsung.com",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2023-07-03T22:00:58.832Z"
},
"n": {
"value": "Samsung Microwave",
"timestamp": "2023-07-03T06:44:54.757Z"
},
"mnmo": {
"value": "TP2X_DA-KS-MICROWAVE-0101X|40436241|50040100011411000200000000000000",
"timestamp": "2023-07-03T06:44:54.757Z"
},
"vid": {
"value": "DA-KS-MICROWAVE-0101X",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"mnpv": {
"value": "DAWIT 3.0",
"timestamp": "2023-07-03T06:44:54.757Z"
},
"mnos": {
"value": "TizenRT 2.0 + IPv6",
"timestamp": "2023-07-03T06:44:54.757Z"
},
"pi": {
"value": "2bad3237-4886-e699-1b90-4a51a3d55c8a",
"timestamp": "2022-03-23T15:59:12.742Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2022-03-23T15:59:12.742Z"
}
},
"samsungce.kitchenDeviceIdentification": {
"regionCode": {
"value": "US",
"timestamp": "2025-02-08T21:13:36.289Z"
},
"modelCode": {
"value": "ME8000T-/AA0",
"timestamp": "2025-02-08T21:13:36.289Z"
},
"fuel": {
"value": null
},
"type": {
"value": "microwave",
"timestamp": "2022-03-23T15:59:10.971Z"
},
"representativeComponent": {
"value": null
}
},
"samsungce.kitchenModeSpecification": {
"specification": {
"value": {
"single": [
{
"mode": "MicroWave",
"supportedOptions": {
"operationTime": {
"max": "01:40:00"
},
"powerLevel": {
"default": "100%",
"supportedValues": [
"0%",
"10%",
"20%",
"30%",
"40%",
"50%",
"60%",
"70%",
"80%",
"90%",
"100%"
]
}
}
},
{
"mode": "ConvectionBake",
"supportedOptions": {
"temperature": {
"F": {
"min": 100,
"max": 425,
"default": 350,
"supportedValues": [
100, 200, 225, 250, 275, 300, 325, 350, 375, 400, 425
]
}
},
"operationTime": {
"max": "01:40:00"
}
}
},
{
"mode": "ConvectionRoast",
"supportedOptions": {
"temperature": {
"F": {
"min": 200,
"max": 425,
"default": 325,
"supportedValues": [
200, 225, 250, 275, 300, 325, 350, 375, 400, 425
]
}
},
"operationTime": {
"max": "01:40:00"
}
}
},
{
"mode": "Grill",
"supportedOptions": {
"temperature": {
"F": {
"min": 425,
"max": 425,
"default": 425,
"resolution": 5
}
},
"operationTime": {
"max": "01:40:00"
}
}
},
{
"mode": "SpeedBake",
"supportedOptions": {
"operationTime": {
"max": "01:40:00"
},
"powerLevel": {
"default": "30%",
"supportedValues": ["10%", "30%", "50%", "70%"]
}
}
},
{
"mode": "SpeedRoast",
"supportedOptions": {
"operationTime": {
"max": "01:40:00"
},
"powerLevel": {
"default": "30%",
"supportedValues": ["10%", "30%", "50%", "70%"]
}
}
},
{
"mode": "KeepWarm",
"supportedOptions": {
"temperature": {
"F": {
"min": 175,
"max": 175,
"default": 175,
"resolution": 5
}
},
"operationTime": {
"max": "01:40:00"
}
}
},
{
"mode": "Autocook",
"supportedOptions": {}
},
{
"mode": "Cookie",
"supportedOptions": {
"temperature": {
"F": {
"min": 325,
"max": 325,
"default": 325,
"resolution": 5
}
},
"operationTime": {
"max": "01:40:00"
}
}
},
{
"mode": "SteamClean",
"supportedOptions": {
"operationTime": {
"max": "00:06:30"
}
}
}
]
},
"timestamp": "2025-02-08T10:21:03.790Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["doorControl", "samsungce.hoodFanSpeed"],
"timestamp": "2025-02-08T21:13:36.152Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 22120101,
"timestamp": "2023-07-03T09:36:13.282Z"
}
},
"sec.diagnosticsInformation": {
"logType": {
"value": ["errCode", "dump"],
"timestamp": "2025-02-08T21:13:36.256Z"
},
"endpoint": {
"value": "SSM",
"timestamp": "2025-02-08T21:13:36.256Z"
},
"minVersion": {
"value": "1.0",
"timestamp": "2025-02-08T21:13:36.256Z"
},
"signinPermission": {
"value": null
},
"setupId": {
"value": "621",
"timestamp": "2025-02-08T21:13:36.256Z"
},
"protocolType": {
"value": "wifi_https",
"timestamp": "2025-02-08T21:13:36.256Z"
},
"tsId": {
"value": null
},
"mnId": {
"value": "0AJT",
"timestamp": "2025-02-08T21:13:36.256Z"
},
"dumpType": {
"value": "file",
"timestamp": "2025-02-08T21:13:36.256Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 1,
"unit": "F",
"timestamp": "2025-02-09T00:11:15.291Z"
}
},
"samsungce.ovenOperatingState": {
"completionTime": {
"value": "2025-02-08T21:13:36.184Z",
"timestamp": "2025-02-08T21:13:36.188Z"
},
"operatingState": {
"value": "ready",
"timestamp": "2025-02-08T21:13:36.188Z"
},
"progress": {
"value": 0,
"timestamp": "2025-02-08T21:13:36.188Z"
},
"ovenJobState": {
"value": "ready",
"timestamp": "2025-02-08T21:13:36.160Z"
},
"operationTime": {
"value": "00:00:00",
"timestamp": "2025-02-08T21:13:36.188Z"
}
},
"ovenMode": {
"supportedOvenModes": {
"value": [
"Microwave",
"ConvectionBake",
"ConvectionRoast",
"grill",
"Others",
"warming"
],
"timestamp": "2025-02-08T10:21:03.790Z"
},
"ovenMode": {
"value": "Others",
"timestamp": "2025-02-08T21:13:36.289Z"
}
},
"samsungce.ovenMode": {
"supportedOvenModes": {
"value": [
"MicroWave",
"ConvectionBake",
"ConvectionRoast",
"Grill",
"SpeedBake",
"SpeedRoast",
"KeepWarm",
"Autocook",
"Cookie",
"SteamClean"
],
"timestamp": "2025-02-08T10:21:03.790Z"
},
"ovenMode": {
"value": "NoOperation",
"timestamp": "2025-02-08T21:13:36.289Z"
}
},
"samsungce.kidsLock": {
"lockState": {
"value": "unlocked",
"timestamp": "2025-02-08T21:13:36.152Z"
}
},
"ovenSetpoint": {
"ovenSetpointRange": {
"value": null
},
"ovenSetpoint": {
"value": 0,
"timestamp": "2025-02-09T00:01:09.108Z"
}
},
"refresh": {},
"samsungce.hoodFanSpeed": {
"settableMaxFanSpeed": {
"value": 3,
"timestamp": "2025-02-09T00:01:06.959Z"
},
"hoodFanSpeed": {
"value": 0,
"timestamp": "2025-02-09T00:01:07.813Z"
},
"supportedHoodFanSpeed": {
"value": [0, 1, 2, 3, 4, 5],
"timestamp": "2022-03-23T15:59:12.796Z"
},
"settableMinFanSpeed": {
"value": 0,
"timestamp": "2025-02-09T00:01:06.959Z"
}
},
"samsungce.doorState": {
"doorState": {
"value": "closed",
"timestamp": "2025-02-08T21:13:36.227Z"
}
},
"samsungce.microwavePower": {
"supportedPowerLevels": {
"value": [
"0%",
"10%",
"20%",
"30%",
"40%",
"50%",
"60%",
"70%",
"80%",
"90%",
"100%"
],
"timestamp": "2025-02-08T21:13:36.160Z"
},
"powerLevel": {
"value": "0%",
"timestamp": "2025-02-08T21:13:36.160Z"
}
},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["x.com.samsung.da.temperatures"],
"if": ["oic.if.baseline", "oic.if.a"],
"x.com.samsung.da.items": [
{
"x.com.samsung.da.id": "0",
"x.com.samsung.da.description": "Temperature",
"x.com.samsung.da.desired": "0",
"x.com.samsung.da.current": "1",
"x.com.samsung.da.increment": "5",
"x.com.samsung.da.unit": "Fahrenheit"
}
]
}
},
"data": {
"href": "/temperatures/vs/0"
},
"timestamp": "2023-07-19T05:50:12.609Z"
}
},
"remoteControlStatus": {
"remoteControlEnabled": {
"value": "false",
"timestamp": "2025-02-08T21:13:36.357Z"
}
},
"samsungce.definedRecipe": {
"definedRecipe": {
"value": {
"cavityId": "0",
"recipeType": "0",
"categoryId": 0,
"itemId": 0,
"servingSize": 0,
"browingLevel": 0,
"option": 0
},
"timestamp": "2025-02-08T21:13:36.160Z"
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": null
},
"otnDUID": {
"value": "U7CNQWBWSCD7C",
"timestamp": "2025-02-08T21:13:36.256Z"
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": [],
"timestamp": "2025-02-08T21:13:36.213Z"
},
"newVersionAvailable": {
"value": false,
"timestamp": "2025-02-08T21:13:36.213Z"
},
"operatingState": {
"value": null
},
"progress": {
"value": null
}
},
"ovenOperatingState": {
"completionTime": {
"value": "2025-02-08T21:13:36.184Z",
"timestamp": "2025-02-08T21:13:36.188Z"
},
"machineState": {
"value": "ready",
"timestamp": "2025-02-08T21:13:36.188Z"
},
"progress": {
"value": 0,
"unit": "%",
"timestamp": "2025-02-08T21:13:36.188Z"
},
"supportedMachineStates": {
"value": null
},
"ovenJobState": {
"value": "ready",
"timestamp": "2025-02-08T21:13:36.160Z"
},
"operationTime": {
"value": 0,
"timestamp": "2025-02-08T21:13:36.188Z"
}
}
},
"hood": {
"samsungce.hoodFanSpeed": {
"settableMaxFanSpeed": {
"value": 3,
"timestamp": "2025-02-09T00:01:06.959Z"
},
"hoodFanSpeed": {
"value": 0,
"timestamp": "2025-02-09T00:01:07.813Z"
},
"supportedHoodFanSpeed": {
"value": [0, 1, 2, 3, 4, 5],
"timestamp": "2022-03-23T15:59:12.796Z"
},
"settableMinFanSpeed": {
"value": 0,
"timestamp": "2025-02-09T00:01:06.959Z"
}
},
"samsungce.lamp": {
"brightnessLevel": {
"value": "off",
"timestamp": "2025-02-08T21:13:36.289Z"
},
"supportedBrightnessLevel": {
"value": ["off", "low", "high"],
"timestamp": "2025-02-08T21:13:36.289Z"
}
}
}
}
}

View File

@ -0,0 +1,727 @@
{
"components": {
"pantry-01": {
"samsungce.fridgePantryInfo": {
"name": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [],
"timestamp": "2022-02-07T10:47:54.524Z"
}
},
"samsungce.fridgePantryMode": {
"mode": {
"value": null
},
"supportedModes": {
"value": null
}
}
},
"pantry-02": {
"samsungce.fridgePantryInfo": {
"name": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [],
"timestamp": "2022-02-07T10:47:54.524Z"
}
},
"samsungce.fridgePantryMode": {
"mode": {
"value": null
},
"supportedModes": {
"value": null
}
}
},
"icemaker": {
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [],
"timestamp": "2024-10-12T13:55:04.008Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-09T13:55:01.720Z"
}
}
},
"onedoor": {
"custom.fridgeMode": {
"fridgeModeValue": {
"value": null
},
"fridgeMode": {
"value": null
},
"supportedFridgeModes": {
"value": null
}
},
"contactSensor": {
"contact": {
"value": null
}
},
"samsungce.unavailableCapabilities": {
"unavailableCommands": {
"value": [],
"timestamp": "2024-11-08T04:14:59.899Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["samsungce.freezerConvertMode", "custom.fridgeMode"],
"timestamp": "2024-11-12T08:23:59.944Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": null
}
},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": null
},
"maximumSetpoint": {
"value": null
}
},
"samsungce.freezerConvertMode": {
"supportedFreezerConvertModes": {
"value": null
},
"freezerConvertMode": {
"value": null
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": null
}
}
},
"cooler": {
"custom.fridgeMode": {
"fridgeModeValue": {
"value": null
},
"fridgeMode": {
"value": null
},
"supportedFridgeModes": {
"value": null
}
},
"contactSensor": {
"contact": {
"value": "closed",
"timestamp": "2025-02-09T16:26:21.425Z"
}
},
"samsungce.unavailableCapabilities": {
"unavailableCommands": {
"value": [],
"timestamp": "2024-11-08T04:14:59.899Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["custom.fridgeMode"],
"timestamp": "2024-10-12T13:55:04.008Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 37,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
}
},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": 34,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
},
"maximumSetpoint": {
"value": 44,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": {
"minimum": 34,
"maximum": 44,
"step": 1
},
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
},
"coolingSetpoint": {
"value": 37,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
}
}
},
"freezer": {
"custom.fridgeMode": {
"fridgeModeValue": {
"value": null
},
"fridgeMode": {
"value": null
},
"supportedFridgeModes": {
"value": null
}
},
"contactSensor": {
"contact": {
"value": "closed",
"timestamp": "2025-02-09T14:48:16.247Z"
}
},
"samsungce.unavailableCapabilities": {
"unavailableCommands": {
"value": [],
"timestamp": "2024-11-08T04:14:59.899Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["custom.fridgeMode", "samsungce.freezerConvertMode"],
"timestamp": "2024-11-08T01:09:17.382Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 0,
"unit": "F",
"timestamp": "2025-01-23T04:42:18.178Z"
}
},
"custom.thermostatSetpointControl": {
"minimumSetpoint": {
"value": -8,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
},
"maximumSetpoint": {
"value": 5,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
}
},
"samsungce.freezerConvertMode": {
"supportedFreezerConvertModes": {
"value": null
},
"freezerConvertMode": {
"value": null
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": {
"minimum": -8,
"maximum": 5,
"step": 1
},
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
},
"coolingSetpoint": {
"value": 0,
"unit": "F",
"timestamp": "2025-01-19T21:07:55.764Z"
}
}
},
"main": {
"contactSensor": {
"contact": {
"value": "closed",
"timestamp": "2025-02-09T16:26:21.425Z"
}
},
"samsungce.dongleSoftwareInstallation": {
"status": {
"value": "completed",
"timestamp": "2022-02-07T10:47:54.524Z"
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": null
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": null
},
"description": {
"value": null
},
"releaseYear": {
"value": 20,
"timestamp": "2024-11-08T01:09:17.382Z"
},
"binaryId": {
"value": "TP2X_REF_20K",
"timestamp": "2025-02-09T13:55:01.720Z"
}
},
"samsungce.quickControl": {
"version": {
"value": null
}
},
"custom.fridgeMode": {
"fridgeModeValue": {
"value": null
},
"fridgeMode": {
"value": null
},
"supportedFridgeModes": {
"value": null
}
},
"ocf": {
"st": {
"value": null
},
"mndt": {
"value": null
},
"mnfv": {
"value": "A-RFWW-TP2-21-COMMON_20220110",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnhw": {
"value": "MediaTek",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"di": {
"value": "7db87911-7dce-1cf2-7119-b953432a2f09",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnsl": {
"value": "http://www.samsung.com",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"n": {
"value": "[refrigerator] Samsung",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnmo": {
"value": "TP2X_REF_20K|00115641|0004014D011411200103000020000000",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"vid": {
"value": "DA-REF-NORMAL-000001",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnpv": {
"value": "DAWIT 2.0",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"mnos": {
"value": "TizenRT 1.0 + IPv6",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"pi": {
"value": "7db87911-7dce-1cf2-7119-b953432a2f09",
"timestamp": "2024-12-21T22:04:22.037Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2024-12-21T22:04:22.037Z"
}
},
"samsungce.fridgeVacationMode": {
"vacationMode": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"temperatureMeasurement",
"thermostatCoolingSetpoint",
"custom.fridgeMode",
"custom.deodorFilter",
"samsungce.dongleSoftwareInstallation",
"samsungce.quickControl",
"samsungce.deviceInfoPrivate",
"demandResponseLoadControl",
"samsungce.fridgeVacationMode",
"sec.diagnosticsInformation"
],
"timestamp": "2025-02-09T13:55:01.720Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 24100101,
"timestamp": "2024-11-08T04:14:59.025Z"
}
},
"sec.diagnosticsInformation": {
"logType": {
"value": null
},
"endpoint": {
"value": null
},
"minVersion": {
"value": null
},
"signinPermission": {
"value": null
},
"setupId": {
"value": null
},
"protocolType": {
"value": null
},
"tsId": {
"value": null
},
"mnId": {
"value": null
},
"dumpType": {
"value": null
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": null
}
},
"custom.deviceReportStateConfiguration": {
"reportStateRealtimePeriod": {
"value": null
},
"reportStateRealtime": {
"value": {
"state": "disabled"
},
"timestamp": "2025-01-19T21:07:55.703Z"
},
"reportStatePeriod": {
"value": "enabled",
"timestamp": "2025-01-19T21:07:55.703Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": null
}
},
"custom.disabledComponents": {
"disabledComponents": {
"value": [
"icemaker-02",
"pantry-01",
"pantry-02",
"cvroom",
"onedoor"
],
"timestamp": "2024-11-08T01:09:17.382Z"
}
},
"demandResponseLoadControl": {
"drlcStatus": {
"value": {
"drlcType": 1,
"drlcLevel": 0,
"duration": 0,
"override": false
},
"timestamp": "2025-01-19T21:07:55.691Z"
}
},
"samsungce.sabbathMode": {
"supportedActions": {
"value": ["on", "off"],
"timestamp": "2025-01-19T21:07:55.799Z"
},
"status": {
"value": "off",
"timestamp": "2025-01-19T21:07:55.799Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": {
"energy": 1568087,
"deltaEnergy": 7,
"power": 6,
"powerEnergy": 13.555977778169844,
"persistedEnergy": 0,
"energySaved": 0,
"start": "2025-02-09T17:38:01Z",
"end": "2025-02-09T17:49:00Z"
},
"timestamp": "2025-02-09T17:49:00.507Z"
}
},
"refresh": {},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["x.com.samsung.da.rm.micomdata"],
"if": ["oic.if.baseline", "oic.if.a"],
"x.com.samsung.rm.micomdata": "D0C0022B00000000000DFE15051F5AA54400000000000000000000000000000000000000000000000001F04A00C5E0",
"x.com.samsung.rm.micomdataLength": 94
}
},
"data": {
"href": "/rm/micomdata/vs/0"
},
"timestamp": "2023-07-19T05:25:39.852Z"
}
},
"refrigeration": {
"defrost": {
"value": "off",
"timestamp": "2025-01-19T21:07:55.772Z"
},
"rapidCooling": {
"value": "off",
"timestamp": "2025-01-19T21:07:55.725Z"
},
"rapidFreezing": {
"value": "off",
"timestamp": "2025-01-19T21:07:55.725Z"
}
},
"custom.deodorFilter": {
"deodorFilterCapacity": {
"value": null
},
"deodorFilterLastResetDate": {
"value": null
},
"deodorFilterStatus": {
"value": null
},
"deodorFilterResetType": {
"value": null
},
"deodorFilterUsage": {
"value": null
},
"deodorFilterUsageStep": {
"value": null
}
},
"samsungce.powerCool": {
"activated": {
"value": false,
"timestamp": "2025-01-19T21:07:55.725Z"
}
},
"custom.energyType": {
"energyType": {
"value": "2.0",
"timestamp": "2022-02-07T10:47:54.524Z"
},
"energySavingSupport": {
"value": false,
"timestamp": "2022-02-07T10:47:54.524Z"
},
"drMaxDuration": {
"value": 1440,
"unit": "min",
"timestamp": "2022-02-07T11:39:47.504Z"
},
"energySavingLevel": {
"value": null
},
"energySavingInfo": {
"value": null
},
"supportedEnergySavingLevels": {
"value": null
},
"energySavingOperation": {
"value": null
},
"notificationTemplateID": {
"value": null
},
"energySavingOperationSupport": {
"value": false,
"timestamp": "2022-02-07T11:39:47.504Z"
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": {},
"timestamp": "2025-01-19T21:07:55.725Z"
},
"otnDUID": {
"value": "P7CNQWBWM3XBW",
"timestamp": "2025-01-19T21:07:55.744Z"
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": [],
"timestamp": "2025-01-19T21:07:55.744Z"
},
"newVersionAvailable": {
"value": false,
"timestamp": "2025-01-19T21:07:55.725Z"
},
"operatingState": {
"value": null
},
"progress": {
"value": null
}
},
"samsungce.powerFreeze": {
"activated": {
"value": false,
"timestamp": "2025-01-19T21:07:55.725Z"
}
},
"custom.waterFilter": {
"waterFilterUsageStep": {
"value": 1,
"timestamp": "2025-01-19T21:07:55.758Z"
},
"waterFilterResetType": {
"value": ["replaceable"],
"timestamp": "2025-01-19T21:07:55.758Z"
},
"waterFilterCapacity": {
"value": null
},
"waterFilterLastResetDate": {
"value": null
},
"waterFilterUsage": {
"value": 100,
"timestamp": "2025-02-09T04:02:12.910Z"
},
"waterFilterStatus": {
"value": "replace",
"timestamp": "2025-02-09T04:02:12.910Z"
}
}
},
"cvroom": {
"custom.fridgeMode": {
"fridgeModeValue": {
"value": null
},
"fridgeMode": {
"value": null
},
"supportedFridgeModes": {
"value": null
}
},
"contactSensor": {
"contact": {
"value": null
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["temperatureMeasurement", "thermostatCoolingSetpoint"],
"timestamp": "2022-02-07T11:39:42.105Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": null
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": null
}
}
},
"icemaker-02": {
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [],
"timestamp": "2022-02-07T11:39:42.105Z"
}
},
"switch": {
"switch": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,274 @@
{
"components": {
"main": {
"custom.disabledComponents": {
"disabledComponents": {
"value": ["station"],
"timestamp": "2020-11-03T04:43:07.114Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": null,
"timestamp": "2020-11-03T04:43:07.092Z"
}
},
"refresh": {},
"samsungce.robotCleanerOperatingState": {
"supportedOperatingState": {
"value": [
"homing",
"error",
"idle",
"charging",
"chargingForRemainingJob",
"paused",
"cleaning"
],
"timestamp": "2020-11-03T04:43:06.547Z"
},
"operatingState": {
"value": "idle",
"timestamp": "2023-06-18T15:59:24.580Z"
},
"cleaningStep": {
"value": null
},
"homingReason": {
"value": "none",
"timestamp": "2020-11-03T04:43:22.926Z"
},
"isMapBasedOperationAvailable": {
"value": null
}
},
"battery": {
"quantity": {
"value": null
},
"battery": {
"value": 100,
"unit": "%",
"timestamp": "2022-09-09T22:55:13.962Z"
},
"type": {
"value": null
}
},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["x.com.samsung.da.alarms"],
"if": ["oic.if.baseline", "oic.if.a"],
"x.com.samsung.da.items": [
{
"x.com.samsung.da.code": "4",
"x.com.samsung.da.alarmType": "Device",
"x.com.samsung.da.triggeredTime": "2023-06-18T15:59:30",
"x.com.samsung.da.state": "deleted"
}
]
}
},
"data": {
"href": "/alarms/vs/0"
},
"timestamp": "2023-06-18T15:59:28.267Z"
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": null
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": null
},
"description": {
"value": null
},
"releaseYear": {
"value": null
},
"binaryId": {
"value": null
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2023-06-18T15:59:27.658Z"
}
},
"robotCleanerTurboMode": {
"robotCleanerTurboMode": {
"value": "off",
"timestamp": "2022-09-08T02:53:49.826Z"
}
},
"ocf": {
"st": {
"value": null,
"timestamp": "2020-06-02T23:30:52.793Z"
},
"mndt": {
"value": null,
"timestamp": "2020-06-03T13:34:18.508Z"
},
"mnfv": {
"value": "1.0",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnhw": {
"value": "1.0",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"di": {
"value": "3442dfc6-17c0-a65f-dae0-4c6e01786f44",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnsl": {
"value": null,
"timestamp": "2020-06-03T00:49:53.813Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2021-12-23T07:09:40.610Z"
},
"n": {
"value": "[robot vacuum] Samsung",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnmo": {
"value": "powerbot_7000_17M|50016055|80010404011141000100000000000000",
"timestamp": "2022-09-07T06:42:36.551Z"
},
"vid": {
"value": "DA-RVC-NORMAL-000001",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnpv": {
"value": "00",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"mnos": {
"value": "Tizen(3/0)",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"pi": {
"value": "3442dfc6-17c0-a65f-dae0-4c6e01786f44",
"timestamp": "2019-07-07T19:45:19.771Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2019-07-07T19:45:19.771Z"
}
},
"samsungce.robotCleanerCleaningMode": {
"supportedCleaningMode": {
"value": ["auto", "spot", "manual", "stop"],
"timestamp": "2020-11-03T04:43:06.547Z"
},
"repeatModeEnabled": {
"value": false,
"timestamp": "2020-12-21T01:32:56.245Z"
},
"supportRepeatMode": {
"value": true,
"timestamp": "2020-11-03T04:43:06.547Z"
},
"cleaningMode": {
"value": "stop",
"timestamp": "2022-09-09T21:25:20.601Z"
}
},
"robotCleanerMovement": {
"robotCleanerMovement": {
"value": "idle",
"timestamp": "2023-06-18T15:59:24.580Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"samsungce.robotCleanerMapAreaInfo",
"samsungce.robotCleanerMapCleaningInfo",
"samsungce.robotCleanerPatrol",
"samsungce.robotCleanerPetMonitoring",
"samsungce.robotCleanerPetMonitoringReport",
"samsungce.robotCleanerPetCleaningSchedule",
"soundDetection",
"samsungce.soundDetectionSensitivity",
"samsungce.musicPlaylist",
"mediaPlayback",
"mediaTrackControl",
"imageCapture",
"videoCapture",
"audioVolume",
"audioMute",
"audioNotification",
"powerConsumptionReport",
"custom.hepaFilter",
"samsungce.robotCleanerMotorFilter",
"samsungce.robotCleanerRelayCleaning",
"audioTrackAddressing",
"samsungce.robotCleanerWelcome"
],
"timestamp": "2022-09-08T01:03:48.820Z"
}
},
"robotCleanerCleaningMode": {
"robotCleanerCleaningMode": {
"value": "stop",
"timestamp": "2022-09-09T21:25:20.601Z"
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": null
},
"otnDUID": {
"value": null
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": null
},
"newVersionAvailable": {
"value": null
},
"operatingState": {
"value": null
},
"progress": {
"value": null
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 22100101,
"timestamp": "2022-11-01T09:26:07.107Z"
}
}
}
}
}

View File

@ -0,0 +1,786 @@
{
"components": {
"main": {
"samsungce.dishwasherWashingCourse": {
"customCourseCandidates": {
"value": null
},
"washingCourse": {
"value": "normal",
"timestamp": "2025-02-08T20:21:26.497Z"
},
"supportedCourses": {
"value": [
"auto",
"normal",
"heavy",
"delicate",
"express",
"rinseOnly",
"selfClean"
],
"timestamp": "2025-02-08T18:00:37.194Z"
}
},
"dishwasherOperatingState": {
"completionTime": {
"value": "2025-02-08T22:49:26Z",
"timestamp": "2025-02-08T20:21:26.452Z"
},
"machineState": {
"value": "stop",
"timestamp": "2025-02-08T20:21:26.452Z"
},
"progress": {
"value": null
},
"supportedMachineStates": {
"value": ["stop", "run", "pause"],
"timestamp": "2024-09-10T10:21:02.853Z"
},
"dishwasherJobState": {
"value": "unknown",
"timestamp": "2025-02-08T20:21:26.452Z"
}
},
"samsungce.dishwasherWashingOptions": {
"dryPlus": {
"value": null
},
"stormWash": {
"value": null
},
"hotAirDry": {
"value": null
},
"selectedZone": {
"value": {
"value": "all",
"settable": ["none", "upper", "lower", "all"]
},
"timestamp": "2022-11-09T00:20:42.461Z"
},
"speedBooster": {
"value": {
"value": false,
"settable": [false, true]
},
"timestamp": "2023-11-24T14:46:55.375Z"
},
"highTempWash": {
"value": {
"value": false,
"settable": [false, true]
},
"timestamp": "2025-02-08T07:39:54.739Z"
},
"sanitizingWash": {
"value": null
},
"heatedDry": {
"value": null
},
"zoneBooster": {
"value": {
"value": "none",
"settable": ["none", "left", "right", "all"]
},
"timestamp": "2022-11-20T07:10:27.445Z"
},
"addRinse": {
"value": null
},
"supportedList": {
"value": [
"selectedZone",
"zoneBooster",
"speedBooster",
"sanitize",
"highTempWash"
],
"timestamp": "2021-06-27T01:19:38.000Z"
},
"rinsePlus": {
"value": null
},
"sanitize": {
"value": {
"value": false,
"settable": [false, true]
},
"timestamp": "2025-01-18T23:49:09.964Z"
},
"steamSoak": {
"value": null
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": null
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": null
},
"description": {
"value": null
},
"releaseYear": {
"value": null
},
"binaryId": {
"value": "DA_DW_A51_20_COMMON",
"timestamp": "2025-02-08T19:29:30.987Z"
}
},
"custom.dishwasherOperatingProgress": {
"dishwasherOperatingProgress": {
"value": "none",
"timestamp": "2025-02-08T20:21:26.452Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-08T20:21:26.386Z"
}
},
"samsungce.quickControl": {
"version": {
"value": null
}
},
"samsungce.waterConsumptionReport": {
"waterConsumption": {
"value": null
}
},
"ocf": {
"st": {
"value": null
},
"mndt": {
"value": null
},
"mnfv": {
"value": "DA_DW_A51_20_COMMON_30230714",
"timestamp": "2023-11-02T15:58:55.699Z"
},
"mnhw": {
"value": "ARTIK051",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"di": {
"value": "f36dc7ce-cac0-0667-dc14-a3704eb5e676",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"mnsl": {
"value": "http://www.samsung.com",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2024-07-04T13:53:32.032Z"
},
"n": {
"value": "[dishwasher] Samsung",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"mnmo": {
"value": "DA_DW_A51_20_COMMON|30007242|40010201001311000101000000000000",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"vid": {
"value": "DA-WM-DW-000001",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"mnpv": {
"value": "DAWIT 2.0",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"mnos": {
"value": "TizenRT 1.0 + IPv6",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"pi": {
"value": "f36dc7ce-cac0-0667-dc14-a3704eb5e676",
"timestamp": "2021-06-27T01:19:37.615Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2021-06-27T01:19:37.615Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"samsungce.waterConsumptionReport",
"sec.wifiConfiguration",
"samsungce.quickControl",
"samsungce.deviceInfoPrivate",
"demandResponseLoadControl",
"sec.diagnosticsInformation",
"custom.waterFilter"
],
"timestamp": "2025-02-08T19:29:32.447Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 24040105,
"timestamp": "2024-07-02T02:56:22.508Z"
}
},
"sec.diagnosticsInformation": {
"logType": {
"value": null
},
"endpoint": {
"value": null
},
"minVersion": {
"value": null
},
"signinPermission": {
"value": null
},
"setupId": {
"value": null
},
"protocolType": {
"value": null
},
"tsId": {
"value": null
},
"mnId": {
"value": null
},
"dumpType": {
"value": null
}
},
"samsungce.dishwasherOperation": {
"supportedOperatingState": {
"value": ["ready", "running", "paused"],
"timestamp": "2024-09-10T10:21:02.853Z"
},
"operatingState": {
"value": "ready",
"timestamp": "2025-02-08T20:21:26.452Z"
},
"reservable": {
"value": false,
"timestamp": "2025-02-08T18:00:37.194Z"
},
"progressPercentage": {
"value": 1,
"timestamp": "2025-02-08T20:21:26.452Z"
},
"remainingTimeStr": {
"value": "02:28",
"timestamp": "2025-02-08T20:21:26.452Z"
},
"operationTime": {
"value": null
},
"remainingTime": {
"value": 148.0,
"unit": "min",
"timestamp": "2025-02-08T20:21:26.452Z"
},
"timeLeftToStart": {
"value": 0.0,
"unit": "min",
"timestamp": "2025-02-08T18:00:37.482Z"
}
},
"samsungce.dishwasherJobState": {
"scheduledJobs": {
"value": [
{
"jobName": "washing",
"timeInSec": 3600
},
{
"jobName": "rinsing",
"timeInSec": 1020
},
{
"jobName": "drying",
"timeInSec": 1200
}
],
"timestamp": "2025-02-08T20:21:26.928Z"
},
"dishwasherJobState": {
"value": "none",
"timestamp": "2025-02-08T20:21:26.452Z"
}
},
"samsungce.kidsLock": {
"lockState": {
"value": "unlocked",
"timestamp": "2025-02-08T18:00:37.450Z"
}
},
"demandResponseLoadControl": {
"drlcStatus": {
"value": null
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": {
"energy": 101600,
"deltaEnergy": 0,
"power": 0,
"powerEnergy": 0.0,
"persistedEnergy": 0,
"energySaved": 0,
"persistedSavedEnergy": 0,
"start": "2025-02-08T20:21:21Z",
"end": "2025-02-08T20:21:26Z"
},
"timestamp": "2025-02-08T20:21:26.596Z"
}
},
"refresh": {},
"samsungce.dishwasherWashingCourseDetails": {
"predefinedCourses": {
"value": [
{
"courseName": "auto",
"energyUsage": 3,
"waterUsage": 3,
"temperature": {
"min": 50,
"max": 60,
"unit": "C"
},
"expectedTime": {
"time": 136,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": [false, true]
},
"sanitize": {
"default": false,
"settable": [false, true]
},
"speedBooster": {
"default": false,
"settable": [false, true]
},
"zoneBooster": {
"default": "none",
"settable": ["none", "left"]
},
"selectedZone": {
"default": "all",
"settable": ["none", "lower", "all"]
}
}
},
{
"courseName": "normal",
"energyUsage": 3,
"waterUsage": 4,
"temperature": {
"min": 45,
"max": 62,
"unit": "C"
},
"expectedTime": {
"time": 148,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": [false, true]
},
"sanitize": {
"default": false,
"settable": [false, true]
},
"speedBooster": {
"default": false,
"settable": [false, true]
},
"zoneBooster": {
"default": "none",
"settable": ["none", "left"]
},
"selectedZone": {
"default": "all",
"settable": ["none", "lower", "all"]
}
}
},
{
"courseName": "heavy",
"energyUsage": 4,
"waterUsage": 5,
"temperature": {
"min": 65,
"max": 65,
"unit": "C"
},
"expectedTime": {
"time": 155,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": [false, true]
},
"sanitize": {
"default": false,
"settable": [false, true]
},
"speedBooster": {
"default": false,
"settable": [false, true]
},
"zoneBooster": {
"default": "none",
"settable": ["none", "left"]
},
"selectedZone": {
"default": "all",
"settable": ["none", "lower", "all"]
}
}
},
{
"courseName": "delicate",
"energyUsage": 2,
"waterUsage": 3,
"temperature": {
"min": 50,
"max": 50,
"unit": "C"
},
"expectedTime": {
"time": 112,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": []
},
"sanitize": {
"default": false,
"settable": []
},
"speedBooster": {
"default": false,
"settable": [false, true]
},
"zoneBooster": {
"default": "none",
"settable": []
},
"selectedZone": {
"default": "all",
"settable": ["none", "lower", "all"]
}
}
},
{
"courseName": "express",
"energyUsage": 2,
"waterUsage": 2,
"temperature": {
"min": 52,
"max": 52,
"unit": "C"
},
"expectedTime": {
"time": 60,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": [false, true]
},
"sanitize": {
"default": false,
"settable": [false, true]
},
"speedBooster": {
"default": false,
"settable": []
},
"zoneBooster": {
"default": "none",
"settable": ["none", "left"]
},
"selectedZone": {
"default": "all",
"settable": ["none", "lower", "all"]
}
}
},
{
"courseName": "rinseOnly",
"energyUsage": 1,
"waterUsage": 1,
"temperature": {
"min": 40,
"max": 40,
"unit": "C"
},
"expectedTime": {
"time": 14,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": []
},
"sanitize": {
"default": false,
"settable": []
},
"speedBooster": {
"default": false,
"settable": []
},
"zoneBooster": {
"default": "none",
"settable": []
},
"selectedZone": {
"default": "all",
"settable": ["none", "lower", "all"]
}
}
},
{
"courseName": "selfClean",
"energyUsage": 5,
"waterUsage": 4,
"temperature": {
"min": 70,
"max": 70,
"unit": "C"
},
"expectedTime": {
"time": 139,
"unit": "min"
},
"options": {
"highTempWash": {
"default": false,
"settable": []
},
"sanitize": {
"default": false,
"settable": []
},
"speedBooster": {
"default": false,
"settable": []
},
"zoneBooster": {
"default": "none",
"settable": []
},
"selectedZone": {
"default": "all",
"settable": ["none", "all"]
}
}
}
],
"timestamp": "2025-02-08T18:00:37.194Z"
},
"waterUsageMax": {
"value": 5,
"timestamp": "2025-02-08T18:00:37.194Z"
},
"energyUsageMax": {
"value": 5,
"timestamp": "2025-02-08T18:00:37.194Z"
}
},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["oic.r.operational.state"],
"if": ["oic.if.baseline", "oic.if.a"],
"currentMachineState": "idle",
"machineStates": ["pause", "active", "idle"],
"jobStates": [
"None",
"Predrain",
"Prewash",
"Wash",
"Rinse",
"Drying",
"Finish"
],
"currentJobState": "None",
"remainingTime": "02:16:00",
"progressPercentage": "1"
}
},
"data": {
"href": "/operational/state/0"
},
"timestamp": "2023-07-19T04:23:15.606Z"
}
},
"sec.wifiConfiguration": {
"autoReconnection": {
"value": null
},
"minVersion": {
"value": null
},
"supportedWiFiFreq": {
"value": null
},
"supportedAuthType": {
"value": null
},
"protocolType": {
"value": null
}
},
"custom.dishwasherOperatingPercentage": {
"dishwasherOperatingPercentage": {
"value": 1,
"timestamp": "2025-02-08T20:21:26.452Z"
}
},
"remoteControlStatus": {
"remoteControlEnabled": {
"value": "false",
"timestamp": "2025-02-08T18:00:37.555Z"
}
},
"custom.supportedOptions": {
"course": {
"value": null
},
"referenceTable": {
"value": null
},
"supportedCourses": {
"value": ["82", "83", "84", "85", "86", "87", "88"],
"timestamp": "2025-02-08T18:00:37.194Z"
}
},
"custom.dishwasherDelayStartTime": {
"dishwasherDelayStartTime": {
"value": "00:00:00",
"timestamp": "2025-02-08T18:00:37.482Z"
}
},
"custom.energyType": {
"energyType": {
"value": "2.0",
"timestamp": "2023-08-25T03:23:06.667Z"
},
"energySavingSupport": {
"value": true,
"timestamp": "2024-10-01T00:08:09.813Z"
},
"drMaxDuration": {
"value": null
},
"energySavingLevel": {
"value": null
},
"energySavingInfo": {
"value": null
},
"supportedEnergySavingLevels": {
"value": null
},
"energySavingOperation": {
"value": null
},
"notificationTemplateID": {
"value": null
},
"energySavingOperationSupport": {
"value": null
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": null
},
"otnDUID": {
"value": "MTCNQWBWIV6TS",
"timestamp": "2025-02-08T18:00:37.538Z"
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": [],
"timestamp": "2022-07-20T03:37:30.706Z"
},
"newVersionAvailable": {
"value": false,
"timestamp": "2025-02-08T18:00:37.538Z"
},
"operatingState": {
"value": null
},
"progress": {
"value": null
}
},
"custom.waterFilter": {
"waterFilterUsageStep": {
"value": null
},
"waterFilterResetType": {
"value": null
},
"waterFilterCapacity": {
"value": null
},
"waterFilterLastResetDate": {
"value": null
},
"waterFilterUsage": {
"value": null
},
"waterFilterStatus": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,719 @@
{
"components": {
"hca.main": {
"hca.dryerMode": {
"mode": {
"value": "normal",
"timestamp": "2025-02-08T18:10:11.023Z"
},
"supportedModes": {
"value": ["normal", "timeDry", "quickDry"],
"timestamp": "2025-02-08T18:10:10.497Z"
}
}
},
"main": {
"custom.dryerWrinklePrevent": {
"operatingState": {
"value": "ready",
"timestamp": "2025-02-08T18:10:10.497Z"
},
"dryerWrinklePrevent": {
"value": "off",
"timestamp": "2025-02-08T18:10:10.840Z"
}
},
"samsungce.dryerDryingTemperature": {
"dryingTemperature": {
"value": "medium",
"timestamp": "2025-02-08T18:10:10.840Z"
},
"supportedDryingTemperature": {
"value": ["none", "extraLow", "low", "mediumLow", "medium", "high"],
"timestamp": "2025-01-04T22:52:14.884Z"
}
},
"samsungce.welcomeMessage": {
"welcomeMessage": {
"value": null
}
},
"samsungce.dongleSoftwareInstallation": {
"status": {
"value": "completed",
"timestamp": "2022-06-14T06:49:02.183Z"
}
},
"samsungce.dryerCyclePreset": {
"maxNumberOfPresets": {
"value": 10,
"timestamp": "2025-02-08T18:10:10.990Z"
},
"presets": {
"value": null
}
},
"samsungce.deviceIdentification": {
"micomAssayCode": {
"value": "20233741",
"timestamp": "2025-02-08T18:10:11.113Z"
},
"modelName": {
"value": null
},
"serialNumber": {
"value": null
},
"serialNumberExtra": {
"value": null
},
"modelClassificationCode": {
"value": "3000000100111100020B000000000000",
"timestamp": "2025-02-08T18:10:11.113Z"
},
"description": {
"value": "DA_WM_A51_20_COMMON_DV6300R/DC92-02385A_0090",
"timestamp": "2025-02-08T18:10:11.113Z"
},
"releaseYear": {
"value": null
},
"binaryId": {
"value": "DA_WM_A51_20_COMMON",
"timestamp": "2025-02-08T18:10:11.113Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-08T18:10:10.911Z"
}
},
"samsungce.quickControl": {
"version": {
"value": null
}
},
"samsungce.dryerFreezePrevent": {
"operatingState": {
"value": null
}
},
"ocf": {
"st": {
"value": null
},
"mndt": {
"value": null
},
"mnfv": {
"value": "DA_WM_A51_20_COMMON_30230708",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnhw": {
"value": "ARTIK051",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"di": {
"value": "02f7256e-8353-5bdd-547f-bd5b1647e01b",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnsl": {
"value": "http://www.samsung.com",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"n": {
"value": "[dryer] Samsung",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnmo": {
"value": "DA_WM_A51_20_COMMON|20233741|3000000100111100020B000000000000",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"vid": {
"value": "DA-WM-WD-000001",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnpv": {
"value": "DAWIT 2.0",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"mnos": {
"value": "TizenRT 1.0 + IPv6",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"pi": {
"value": "02f7256e-8353-5bdd-547f-bd5b1647e01b",
"timestamp": "2025-01-04T22:52:14.222Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2025-01-04T22:52:14.222Z"
}
},
"custom.dryerDryLevel": {
"dryerDryLevel": {
"value": "normal",
"timestamp": "2025-02-08T18:10:10.840Z"
},
"supportedDryerDryLevel": {
"value": ["none", "damp", "less", "normal", "more", "very"],
"timestamp": "2021-06-01T22:54:28.224Z"
}
},
"samsungce.dryerAutoCycleLink": {
"dryerAutoCycleLink": {
"value": "on",
"timestamp": "2025-02-08T18:10:11.986Z"
}
},
"samsungce.dryerCycle": {
"dryerCycle": {
"value": "Table_00_Course_01",
"timestamp": "2025-02-08T18:10:11.023Z"
},
"supportedCycles": {
"value": [
{
"cycle": "01",
"supportedOptions": {
"dryingLevel": {
"raw": "D33E",
"default": "normal",
"options": ["damp", "less", "normal", "more", "very"]
},
"dryingTemperature": {
"raw": "8410",
"default": "medium",
"options": ["medium"]
}
}
},
{
"cycle": "9C",
"supportedOptions": {
"dryingLevel": {
"raw": "D33E",
"default": "normal",
"options": ["damp", "less", "normal", "more", "very"]
},
"dryingTemperature": {
"raw": "8520",
"default": "high",
"options": ["high"]
}
}
},
{
"cycle": "A5",
"supportedOptions": {
"dryingLevel": {
"raw": "D33E",
"default": "normal",
"options": ["damp", "less", "normal", "more", "very"]
},
"dryingTemperature": {
"raw": "8520",
"default": "high",
"options": ["high"]
}
}
},
{
"cycle": "9E",
"supportedOptions": {
"dryingLevel": {
"raw": "D33E",
"default": "normal",
"options": ["damp", "less", "normal", "more", "very"]
},
"dryingTemperature": {
"raw": "8308",
"default": "mediumLow",
"options": ["mediumLow"]
}
}
},
{
"cycle": "9B",
"supportedOptions": {
"dryingLevel": {
"raw": "D520",
"default": "very",
"options": ["very"]
},
"dryingTemperature": {
"raw": "8520",
"default": "high",
"options": ["high"]
}
}
},
{
"cycle": "27",
"supportedOptions": {
"dryingLevel": {
"raw": "D000",
"default": "none",
"options": []
},
"dryingTemperature": {
"raw": "8520",
"default": "high",
"options": ["high"]
}
}
},
{
"cycle": "E5",
"supportedOptions": {
"dryingLevel": {
"raw": "D000",
"default": "none",
"options": []
},
"dryingTemperature": {
"raw": "8000",
"default": "none",
"options": []
}
}
},
{
"cycle": "A0",
"supportedOptions": {
"dryingLevel": {
"raw": "D000",
"default": "none",
"options": []
},
"dryingTemperature": {
"raw": "8000",
"default": "none",
"options": []
}
}
},
{
"cycle": "A4",
"supportedOptions": {
"dryingLevel": {
"raw": "D000",
"default": "none",
"options": []
},
"dryingTemperature": {
"raw": "853E",
"default": "high",
"options": ["extraLow", "low", "mediumLow", "medium", "high"]
}
}
},
{
"cycle": "A6",
"supportedOptions": {
"dryingLevel": {
"raw": "D000",
"default": "none",
"options": []
},
"dryingTemperature": {
"raw": "8520",
"default": "high",
"options": ["high"]
}
}
},
{
"cycle": "A3",
"supportedOptions": {
"dryingLevel": {
"raw": "D308",
"default": "normal",
"options": ["normal"]
},
"dryingTemperature": {
"raw": "8410",
"default": "medium",
"options": ["medium"]
}
}
},
{
"cycle": "A2",
"supportedOptions": {
"dryingLevel": {
"raw": "D33E",
"default": "normal",
"options": ["damp", "less", "normal", "more", "very"]
},
"dryingTemperature": {
"raw": "8102",
"default": "extraLow",
"options": ["extraLow"]
}
}
}
],
"timestamp": "2025-01-04T22:52:14.884Z"
},
"referenceTable": {
"value": {
"id": "Table_00"
},
"timestamp": "2025-02-08T18:10:11.023Z"
},
"specializedFunctionClassification": {
"value": 4,
"timestamp": "2025-02-08T18:10:10.497Z"
}
},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": [
"samsungce.dryerCyclePreset",
"samsungce.welcomeMessage",
"samsungce.dongleSoftwareInstallation",
"sec.wifiConfiguration",
"samsungce.quickControl",
"samsungce.deviceInfoPrivate",
"demandResponseLoadControl",
"samsungce.dryerFreezePrevent",
"sec.diagnosticsInformation"
],
"timestamp": "2024-07-05T16:04:06.674Z"
}
},
"samsungce.driverVersion": {
"versionNumber": {
"value": 24110101,
"timestamp": "2024-12-03T02:59:11.115Z"
}
},
"sec.diagnosticsInformation": {
"logType": {
"value": null
},
"endpoint": {
"value": null
},
"minVersion": {
"value": null
},
"signinPermission": {
"value": null
},
"setupId": {
"value": null
},
"protocolType": {
"value": null
},
"tsId": {
"value": null
},
"mnId": {
"value": null
},
"dumpType": {
"value": null
}
},
"samsungce.kidsLock": {
"lockState": {
"value": "unlocked",
"timestamp": "2025-02-08T18:10:10.825Z"
}
},
"demandResponseLoadControl": {
"drlcStatus": {
"value": null
}
},
"samsungce.detergentOrder": {
"alarmEnabled": {
"value": false,
"timestamp": "2025-02-08T18:10:10.497Z"
},
"orderThreshold": {
"value": 0,
"unit": "cc",
"timestamp": "2025-02-08T18:10:10.497Z"
}
},
"powerConsumptionReport": {
"powerConsumption": {
"value": {
"energy": 4495500,
"deltaEnergy": 0,
"power": 0,
"powerEnergy": 0.0,
"persistedEnergy": 0,
"energySaved": 0,
"start": "2025-02-07T04:00:19Z",
"end": "2025-02-08T18:10:11Z"
},
"timestamp": "2025-02-08T18:10:11.053Z"
}
},
"dryerOperatingState": {
"completionTime": {
"value": "2025-02-08T19:25:10Z",
"timestamp": "2025-02-08T18:10:10.962Z"
},
"machineState": {
"value": "stop",
"timestamp": "2025-02-08T18:10:10.962Z"
},
"supportedMachineStates": {
"value": ["stop", "run", "pause"],
"timestamp": "2025-02-08T18:10:10.962Z"
},
"dryerJobState": {
"value": "none",
"timestamp": "2025-02-08T18:10:10.962Z"
}
},
"samsungce.detergentState": {
"remainingAmount": {
"value": 0,
"unit": "cc",
"timestamp": "2025-02-08T18:10:10.497Z"
},
"dosage": {
"value": 0,
"unit": "cc",
"timestamp": "2025-02-08T18:10:10.497Z"
},
"initialAmount": {
"value": 0,
"unit": "cc",
"timestamp": "2025-02-08T18:10:10.497Z"
},
"detergentType": {
"value": "none",
"timestamp": "2021-06-01T22:54:28.372Z"
}
},
"samsungce.dryerDelayEnd": {
"remainingTime": {
"value": 0,
"unit": "min",
"timestamp": "2025-02-08T18:10:10.962Z"
}
},
"refresh": {},
"custom.jobBeginningStatus": {
"jobBeginningStatus": {
"value": null
}
},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["x.com.samsung.da.information"],
"if": ["oic.if.baseline", "oic.if.a"],
"x.com.samsung.da.modelNum": "DA_WM_A51_20_COMMON|20233741|3000000100111100020B000000000000",
"x.com.samsung.da.description": "DA_WM_A51_20_COMMON_DV6300R/DC92-02385A_0090",
"x.com.samsung.da.serialNum": "FFFFFFFFFFFFFFF",
"x.com.samsung.da.otnDUID": "7XCDM6YAIRCGM",
"x.com.samsung.da.items": [
{
"x.com.samsung.da.id": "0",
"x.com.samsung.da.description": "DA_WM_A51_20_COMMON|20233741|3000000100111100020B000000000000",
"x.com.samsung.da.type": "Software",
"x.com.samsung.da.number": "02198A220728(E256)",
"x.com.samsung.da.newVersionAvailable": "0"
},
{
"x.com.samsung.da.id": "1",
"x.com.samsung.da.description": "DA_WM_A51_20_COMMON",
"x.com.samsung.da.type": "Firmware",
"x.com.samsung.da.number": "18112816,20112625",
"x.com.samsung.da.newVersionAvailable": "0"
}
]
}
},
"data": {
"href": "/information/vs/0"
},
"timestamp": "2023-08-06T22:48:43.192Z"
}
},
"sec.wifiConfiguration": {
"autoReconnection": {
"value": null
},
"minVersion": {
"value": null
},
"supportedWiFiFreq": {
"value": null
},
"supportedAuthType": {
"value": null
},
"protocolType": {
"value": null
}
},
"remoteControlStatus": {
"remoteControlEnabled": {
"value": "false",
"timestamp": "2025-02-08T18:10:10.970Z"
}
},
"custom.supportedOptions": {
"course": {
"value": null
},
"referenceTable": {
"value": {
"id": "Table_00"
},
"timestamp": "2025-02-08T18:10:11.023Z"
},
"supportedCourses": {
"value": [
"01",
"9C",
"A5",
"9E",
"9B",
"27",
"E5",
"A0",
"A4",
"A6",
"A3",
"A2"
],
"timestamp": "2025-02-08T18:10:10.497Z"
}
},
"custom.energyType": {
"energyType": {
"value": "2.0",
"timestamp": "2022-06-14T06:49:02.183Z"
},
"energySavingSupport": {
"value": false,
"timestamp": "2022-06-14T06:49:02.721Z"
},
"drMaxDuration": {
"value": null
},
"energySavingLevel": {
"value": null
},
"energySavingInfo": {
"value": null
},
"supportedEnergySavingLevels": {
"value": null
},
"energySavingOperation": {
"value": null
},
"notificationTemplateID": {
"value": null
},
"energySavingOperationSupport": {
"value": null
}
},
"samsungce.dryerOperatingState": {
"operatingState": {
"value": "ready",
"timestamp": "2025-02-07T04:00:18.186Z"
},
"supportedOperatingStates": {
"value": ["ready", "running", "paused"],
"timestamp": "2022-11-01T13:43:26.961Z"
},
"scheduledJobs": {
"value": [
{
"jobName": "drying",
"timeInMin": 57
},
{
"jobName": "cooling",
"timeInMin": 3
}
],
"timestamp": "2025-02-08T18:10:10.497Z"
},
"progress": {
"value": 1,
"unit": "%",
"timestamp": "2025-02-07T04:00:18.186Z"
},
"remainingTimeStr": {
"value": "01:15",
"timestamp": "2025-02-07T04:00:18.186Z"
},
"dryerJobState": {
"value": "none",
"timestamp": "2025-02-07T04:00:18.186Z"
},
"remainingTime": {
"value": 75,
"unit": "min",
"timestamp": "2025-02-07T04:00:18.186Z"
}
},
"samsungce.softwareUpdate": {
"targetModule": {
"value": null
},
"otnDUID": {
"value": "7XCDM6YAIRCGM",
"timestamp": "2025-02-08T18:10:11.113Z"
},
"lastUpdatedDate": {
"value": null
},
"availableModules": {
"value": [],
"timestamp": "2024-12-02T00:29:53.432Z"
},
"newVersionAvailable": {
"value": false,
"timestamp": "2024-12-02T00:29:53.432Z"
},
"operatingState": {
"value": null
},
"progress": {
"value": null
}
},
"samsungce.dryerDryingTime": {
"supportedDryingTime": {
"value": ["0", "20", "30", "40", "50", "60"],
"timestamp": "2021-06-01T22:54:28.224Z"
},
"dryingTime": {
"value": "0",
"unit": "min",
"timestamp": "2025-02-08T18:10:10.840Z"
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
{
"components": {
"main": {
"presenceSensor": {
"presence": {
"value": "not present",
"timestamp": "2025-02-11T13:58:50.044Z"
}
},
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2025-01-16T21:14:07.471Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "online",
"data": {},
"timestamp": "2025-02-11T14:23:22.053Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 71,
"unit": "F",
"timestamp": "2025-02-11T14:36:16.823Z"
}
},
"refresh": {},
"motionSensor": {
"motion": {
"value": "inactive",
"timestamp": "2025-02-11T13:58:50.044Z"
}
}
}
}
}

View File

@ -0,0 +1,98 @@
{
"components": {
"main": {
"relativeHumidityMeasurement": {
"humidity": {
"value": 32,
"unit": "%",
"timestamp": "2025-02-11T14:36:17.275Z"
}
},
"thermostatOperatingState": {
"thermostatOperatingState": {
"value": "heating",
"timestamp": "2025-02-11T13:39:58.286Z"
}
},
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2025-01-16T21:14:07.448Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "online",
"data": {},
"timestamp": "2025-02-11T13:39:58.286Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 71,
"unit": "F",
"timestamp": "2025-02-11T14:23:21.556Z"
}
},
"thermostatHeatingSetpoint": {
"heatingSetpoint": {
"value": 71,
"unit": "F",
"timestamp": "2025-02-11T13:39:58.286Z"
},
"heatingSetpointRange": {
"value": null
}
},
"thermostatFanMode": {
"thermostatFanMode": {
"value": "auto",
"data": {
"supportedThermostatFanModes": ["on", "auto"]
},
"timestamp": "2025-02-11T13:39:58.286Z"
},
"supportedThermostatFanModes": {
"value": ["on", "auto"],
"timestamp": "2025-02-11T13:39:58.286Z"
}
},
"refresh": {},
"thermostatMode": {
"thermostatMode": {
"value": "heat",
"data": {
"supportedThermostatModes": ["off", "cool", "auxheatonly", "auto"]
},
"timestamp": "2025-02-11T13:39:58.286Z"
},
"supportedThermostatModes": {
"value": ["off", "cool", "auxheatonly", "auto"],
"timestamp": "2025-02-11T13:39:58.286Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": 73,
"unit": "F",
"timestamp": "2025-02-11T13:39:58.286Z"
}
}
}
}
}

View File

@ -0,0 +1,31 @@
{
"components": {
"main": {
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-08T23:21:22.908Z"
}
},
"fanSpeed": {
"fanSpeed": {
"value": 60,
"timestamp": "2025-02-10T21:09:08.357Z"
}
},
"airConditionerFanMode": {
"fanMode": {
"value": null,
"timestamp": "2021-04-06T16:44:10.381Z"
},
"supportedAcFanModes": {
"value": ["auto", "low", "medium", "high", "turbo"],
"timestamp": "2024-09-10T10:26:28.605Z"
},
"availableAcFanModes": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"components": {
"main": {
"switchLevel": {
"levelRange": {
"value": null
},
"level": {
"value": 39,
"unit": "%",
"timestamp": "2025-02-07T02:39:25.819Z"
}
},
"refresh": {},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-08T23:21:22.908Z"
}
}
}
}
}

View File

@ -0,0 +1,75 @@
{
"components": {
"main": {
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2023-12-17T18:11:41.671Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "online",
"data": {},
"timestamp": "2025-02-07T15:14:53.823Z"
}
},
"switchLevel": {
"levelRange": {
"value": {
"minimum": 1,
"maximum": 100
},
"unit": "%",
"timestamp": "2025-02-07T15:14:53.823Z"
},
"level": {
"value": 70,
"unit": "%",
"timestamp": "2025-02-07T21:56:04.127Z"
}
},
"refresh": {},
"synthetic.lightingEffectFade": {
"fade": {
"value": null
}
},
"colorTemperature": {
"colorTemperatureRange": {
"value": {
"minimum": 2000,
"maximum": 6535
},
"unit": "K",
"timestamp": "2025-02-07T15:14:53.823Z"
},
"colorTemperature": {
"value": 3000,
"unit": "K",
"timestamp": "2025-02-07T21:56:04.127Z"
}
},
"switch": {
"switch": {
"value": "on",
"timestamp": "2025-02-07T21:56:04.127Z"
}
},
"synthetic.lightingEffectCircadian": {
"circadian": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,94 @@
{
"components": {
"main": {
"colorControl": {
"saturation": {
"value": 60,
"timestamp": "2025-02-07T15:14:53.812Z"
},
"color": {
"value": null
},
"hue": {
"value": 60.8072,
"timestamp": "2025-02-07T15:14:53.812Z"
}
},
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2023-12-17T18:11:41.678Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "online",
"data": {},
"timestamp": "2025-02-07T15:14:53.812Z"
}
},
"switchLevel": {
"levelRange": {
"value": {
"minimum": 1,
"maximum": 100
},
"unit": "%",
"timestamp": "2025-02-07T15:14:53.812Z"
},
"level": {
"value": 70,
"unit": "%",
"timestamp": "2025-02-07T21:56:02.381Z"
}
},
"refresh": {},
"synthetic.lightingEffectFade": {
"fade": {
"value": null
}
},
"samsungim.hueSyncMode": {
"mode": {
"value": "normal",
"timestamp": "2025-02-07T15:14:53.812Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-08T07:08:19.519Z"
}
},
"colorTemperature": {
"colorTemperatureRange": {
"value": {
"minimum": 2000,
"maximum": 6535
},
"unit": "K",
"timestamp": "2025-02-06T15:14:52.807Z"
},
"colorTemperature": {
"value": 3000,
"unit": "K",
"timestamp": "2025-02-07T21:56:02.381Z"
}
},
"synthetic.lightingEffectCircadian": {
"circadian": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,12 @@
{
"components": {
"main": {
"presenceSensor": {
"presence": {
"value": "present",
"timestamp": "2023-09-22T18:12:25.012Z"
}
}
}
}
}

View File

@ -0,0 +1,79 @@
{
"components": {
"main": {
"contactSensor": {
"contact": {
"value": "closed",
"timestamp": "2025-02-08T14:00:28.332Z"
}
},
"threeAxis": {
"threeAxis": {
"value": [20, 8, -1042],
"unit": "mG",
"timestamp": "2025-02-09T17:27:36.673Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": null
},
"temperature": {
"value": 67.0,
"unit": "F",
"timestamp": "2025-02-09T17:56:19.744Z"
}
},
"refresh": {},
"battery": {
"quantity": {
"value": null
},
"battery": {
"value": 50,
"unit": "%",
"timestamp": "2025-02-09T12:24:02.074Z"
},
"type": {
"value": null
}
},
"firmwareUpdate": {
"lastUpdateStatusReason": {
"value": null
},
"availableVersion": {
"value": "0000001B",
"timestamp": "2025-02-09T04:20:25.600Z"
},
"lastUpdateStatus": {
"value": null
},
"supportedCommands": {
"value": null
},
"state": {
"value": "normalOperation",
"timestamp": "2025-02-09T04:20:25.600Z"
},
"updateAvailable": {
"value": false,
"timestamp": "2025-02-09T04:20:25.601Z"
},
"currentVersion": {
"value": "0000001B",
"timestamp": "2025-02-09T04:20:25.593Z"
},
"lastUpdateTime": {
"value": null
}
},
"accelerationSensor": {
"acceleration": {
"value": "inactive",
"timestamp": "2025-02-09T17:27:46.812Z"
}
}
}
}
}

View File

@ -0,0 +1,57 @@
{
"components": {
"main": {
"healthCheck": {
"checkInterval": {
"value": 60,
"unit": "s",
"data": {
"deviceScheme": "UNTRACKED",
"protocol": "cloud"
},
"timestamp": "2024-12-04T10:10:02.934Z"
},
"healthStatus": {
"value": null
},
"DeviceWatch-Enroll": {
"value": null
},
"DeviceWatch-DeviceStatus": {
"value": "online",
"data": {},
"timestamp": "2025-02-09T10:09:47.758Z"
}
},
"refresh": {},
"airConditionerMode": {
"availableAcModes": {
"value": null
},
"supportedAcModes": {
"value": null
},
"airConditionerMode": {
"value": "cool",
"timestamp": "2025-02-09T10:09:47.758Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": null
},
"coolingSetpoint": {
"value": 20,
"unit": "C",
"timestamp": "2025-02-09T10:09:47.758Z"
}
},
"switch": {
"switch": {
"value": "off",
"timestamp": "2025-02-09T10:09:47.758Z"
}
}
}
}
}

View File

@ -0,0 +1,43 @@
{
"components": {
"main": {
"refresh": {},
"firmwareUpdate": {
"lastUpdateStatusReason": {
"value": null
},
"availableVersion": {
"value": "00102101",
"timestamp": "2025-02-08T19:37:03.624Z"
},
"lastUpdateStatus": {
"value": null
},
"supportedCommands": {
"value": null
},
"state": {
"value": "normalOperation",
"timestamp": "2025-02-08T19:37:03.622Z"
},
"updateAvailable": {
"value": false,
"timestamp": "2025-02-08T19:37:03.624Z"
},
"currentVersion": {
"value": "00102101",
"timestamp": "2025-02-08T19:37:03.594Z"
},
"lastUpdateTime": {
"value": null
}
},
"switch": {
"switch": {
"value": "on",
"timestamp": "2025-02-09T17:31:12.210Z"
}
}
}
}
}

View File

@ -0,0 +1,259 @@
{
"components": {
"main": {
"mediaPlayback": {
"supportedPlaybackCommands": {
"value": ["play", "pause", "stop"],
"timestamp": "2025-02-02T13:18:40.078Z"
},
"playbackStatus": {
"value": "playing",
"timestamp": "2025-02-09T19:53:58.330Z"
}
},
"mediaPresets": {
"presets": {
"value": [
{
"id": "10",
"imageUrl": "https://www.storytel.com//images/320x320/0000059036.jpg",
"mediaSource": "Storytel",
"name": "Dra \u00e5t skogen Sune!"
},
{
"id": "22",
"imageUrl": "https://www.storytel.com//images/320x320/0000001894.jpg",
"mediaSource": "Storytel",
"name": "Fy katten Sune"
},
{
"id": "29",
"imageUrl": "https://www.storytel.com//images/320x320/0000001896.jpg",
"mediaSource": "Storytel",
"name": "Gult \u00e4r fult, Sune"
},
{
"id": "2",
"imageUrl": "https://static.mytuner.mobi/media/tvos_radios/2l5zg6lhjbab.png",
"mediaSource": "myTuner Radio",
"name": "Kiss"
},
{
"id": "3",
"imageUrl": "https://www.storytel.com//images/320x320/0000046017.jpg",
"mediaSource": "Storytel",
"name": "L\u00e4skigt Sune!"
},
{
"id": "16",
"imageUrl": "https://www.storytel.com//images/320x320/0002590598.jpg",
"mediaSource": "Storytel",
"name": "Pluggh\u00e4sten Sune"
},
{
"id": "14",
"imageUrl": "https://www.storytel.com//images/320x320/0000000070.jpg",
"mediaSource": "Storytel",
"name": "Sagan om Sune"
},
{
"id": "18",
"imageUrl": "https://www.storytel.com//images/320x320/0000006452.jpg",
"mediaSource": "Storytel",
"name": "Sk\u00e4mtaren Sune"
},
{
"id": "26",
"imageUrl": "https://www.storytel.com//images/320x320/0000001892.jpg",
"mediaSource": "Storytel",
"name": "Spik och panik, Sune!"
},
{
"id": "7",
"imageUrl": "https://www.storytel.com//images/320x320/0003119145.jpg",
"mediaSource": "Storytel",
"name": "Sune - T\u00e5gsemestern"
},
{
"id": "25",
"imageUrl": "https://www.storytel.com//images/320x320/0000000071.jpg",
"mediaSource": "Storytel",
"name": "Sune b\u00f6rjar tv\u00e5an"
},
{
"id": "9",
"imageUrl": "https://www.storytel.com//images/320x320/0000006448.jpg",
"mediaSource": "Storytel",
"name": "Sune i Grekland"
},
{
"id": "8",
"imageUrl": "https://www.storytel.com//images/320x320/0002492498.jpg",
"mediaSource": "Storytel",
"name": "Sune i Ullared"
},
{
"id": "30",
"imageUrl": "https://www.storytel.com//images/320x320/0002072946.jpg",
"mediaSource": "Storytel",
"name": "Sune och familjen Anderssons sjuka jul"
},
{
"id": "17",
"imageUrl": "https://www.storytel.com//images/320x320/0000000475.jpg",
"mediaSource": "Storytel",
"name": "Sune och klantpappan"
},
{
"id": "11",
"imageUrl": "https://www.storytel.com//images/320x320/0000042688.jpg",
"mediaSource": "Storytel",
"name": "Sune och Mamma Mysko"
},
{
"id": "20",
"imageUrl": "https://www.storytel.com//images/320x320/0000000072.jpg",
"mediaSource": "Storytel",
"name": "Sune och syster vampyr"
},
{
"id": "15",
"imageUrl": "https://www.storytel.com//images/320x320/0000039918.jpg",
"mediaSource": "Storytel",
"name": "Sune slutar f\u00f6rsta klass"
},
{
"id": "5",
"imageUrl": "https://www.storytel.com//images/320x320/0000017431.jpg",
"mediaSource": "Storytel",
"name": "Sune v\u00e4rsta killen!"
},
{
"id": "27",
"imageUrl": "https://www.storytel.com//images/320x320/0000068900.jpg",
"mediaSource": "Storytel",
"name": "Sunes halloween"
},
{
"id": "19",
"imageUrl": "https://www.storytel.com//images/320x320/0000000476.jpg",
"mediaSource": "Storytel",
"name": "Sunes hemligheter"
},
{
"id": "21",
"imageUrl": "https://www.storytel.com//images/320x320/0002370989.jpg",
"mediaSource": "Storytel",
"name": "Sunes hj\u00e4rnsl\u00e4pp"
},
{
"id": "24",
"imageUrl": "https://www.storytel.com//images/320x320/0000001889.jpg",
"mediaSource": "Storytel",
"name": "Sunes jul"
},
{
"id": "28",
"imageUrl": "https://www.storytel.com//images/320x320/0000034437.jpg",
"mediaSource": "Storytel",
"name": "Sunes party"
},
{
"id": "4",
"imageUrl": "https://www.storytel.com//images/320x320/0000006450.jpg",
"mediaSource": "Storytel",
"name": "Sunes skolresa"
},
{
"id": "13",
"imageUrl": "https://www.storytel.com//images/320x320/0000000477.jpg",
"mediaSource": "Storytel",
"name": "Sunes sommar"
},
{
"id": "12",
"imageUrl": "https://www.storytel.com//images/320x320/0000046015.jpg",
"mediaSource": "Storytel",
"name": "Sunes Sommarstuga"
},
{
"id": "6",
"imageUrl": "https://www.storytel.com//images/320x320/0002099327.jpg",
"mediaSource": "Storytel",
"name": "Supersnuten Sune"
},
{
"id": "23",
"imageUrl": "https://www.storytel.com//images/320x320/0000563738.jpg",
"mediaSource": "Storytel",
"name": "Zunes stolpskott"
}
],
"timestamp": "2025-02-02T13:18:48.272Z"
}
},
"audioVolume": {
"volume": {
"value": 15,
"unit": "%",
"timestamp": "2025-02-09T19:57:37.230Z"
}
},
"mediaGroup": {
"groupMute": {
"value": "unmuted",
"timestamp": "2025-02-07T01:19:54.911Z"
},
"groupPrimaryDeviceId": {
"value": "RINCON_38420B9108F601400",
"timestamp": "2025-02-09T19:52:24.000Z"
},
"groupId": {
"value": "RINCON_38420B9108F601400:3579458382",
"timestamp": "2025-02-09T19:54:06.936Z"
},
"groupVolume": {
"value": 12,
"unit": "%",
"timestamp": "2025-02-07T01:19:54.911Z"
},
"groupRole": {
"value": "ungrouped",
"timestamp": "2025-02-09T19:52:23.974Z"
}
},
"refresh": {},
"mediaTrackControl": {
"supportedTrackControlCommands": {
"value": ["nextTrack", "previousTrack"],
"timestamp": "2025-02-02T13:18:40.123Z"
}
},
"audioMute": {
"mute": {
"value": "unmuted",
"timestamp": "2025-02-09T19:57:35.487Z"
}
},
"audioNotification": {},
"audioTrackData": {
"totalTime": {
"value": null
},
"audioTrackData": {
"value": {
"album": "Forever Young",
"albumArtUrl": "http://192.168.1.123:1400/getaa?s=1&u=x-sonos-spotify%3aspotify%253atrack%253a3bg2qahpZmsg5wV2EMPXIk%3fsid%3d9%26flags%3d8232%26sn%3d9",
"artist": "David Guetta",
"mediaSource": "Spotify",
"title": "Forever Young"
},
"timestamp": "2025-02-09T19:53:55.615Z"
},
"elapsedTime": {
"value": null
}
}
}
}
}

View File

@ -0,0 +1,164 @@
{
"components": {
"main": {
"mediaPlayback": {
"supportedPlaybackCommands": {
"value": ["play", "pause", "stop"],
"timestamp": "2025-02-09T15:42:12.923Z"
},
"playbackStatus": {
"value": "stopped",
"timestamp": "2025-02-09T15:42:12.923Z"
}
},
"samsungvd.soundFrom": {
"mode": {
"value": 3,
"timestamp": "2025-02-09T15:42:13.215Z"
},
"detailName": {
"value": "External Device",
"timestamp": "2025-02-09T15:42:13.215Z"
}
},
"audioVolume": {
"volume": {
"value": 17,
"unit": "%",
"timestamp": "2025-02-09T17:25:51.839Z"
}
},
"samsungvd.audioGroupInfo": {
"role": {
"value": null
},
"status": {
"value": null
}
},
"refresh": {},
"audioNotification": {},
"execute": {
"data": {
"value": null
}
},
"samsungvd.audioInputSource": {
"supportedInputSources": {
"value": ["digital", "HDMI1", "bluetooth", "wifi", "HDMI2"],
"timestamp": "2025-02-09T17:18:44.680Z"
},
"inputSource": {
"value": "HDMI1",
"timestamp": "2025-02-09T17:18:44.680Z"
}
},
"switch": {
"switch": {
"value": "on",
"timestamp": "2025-02-09T17:25:51.536Z"
}
},
"ocf": {
"st": {
"value": "2024-12-10T02:12:44Z",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mndt": {
"value": "2023-01-01",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnfv": {
"value": "SAT-iMX8M23WWC-1010.5",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnhw": {
"value": "",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"di": {
"value": "0d94e5db-8501-2355-eb4f-214163702cac",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnsl": {
"value": "",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"n": {
"value": "Soundbar Living",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnmo": {
"value": "HW-Q990C",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"vid": {
"value": "VD-NetworkAudio-002S",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnml": {
"value": "",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnpv": {
"value": "7.0",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"mnos": {
"value": "Tizen",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"pi": {
"value": "0d94e5db-8501-2355-eb4f-214163702cac",
"timestamp": "2024-12-31T01:03:42.587Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2024-12-31T01:03:42.587Z"
}
},
"audioMute": {
"mute": {
"value": "unmuted",
"timestamp": "2025-02-09T17:18:44.787Z"
}
},
"samsungvd.thingStatus": {
"updatedTime": {
"value": 1739115734,
"timestamp": "2025-02-09T15:42:13.949Z"
},
"status": {
"value": "Idle",
"timestamp": "2025-02-09T15:42:13.949Z"
}
},
"audioTrackData": {
"totalTime": {
"value": 0,
"timestamp": "2024-12-31T00:29:29.953Z"
},
"audioTrackData": {
"value": {
"title": "",
"artist": "",
"album": ""
},
"timestamp": "2024-12-31T00:29:29.953Z"
},
"elapsedTime": {
"value": 0,
"timestamp": "2024-12-31T00:29:29.828Z"
}
}
}
}
}

View File

@ -0,0 +1,266 @@
{
"components": {
"main": {
"mediaPlayback": {
"supportedPlaybackCommands": {
"value": ["play", "pause", "stop", "fastForward", "rewind"],
"timestamp": "2020-05-07T02:58:10.250Z"
},
"playbackStatus": {
"value": null,
"timestamp": "2020-08-04T21:53:22.108Z"
}
},
"audioVolume": {
"volume": {
"value": 13,
"unit": "%",
"timestamp": "2021-08-21T19:19:52.832Z"
}
},
"samsungvd.supportsPowerOnByOcf": {
"supportsPowerOnByOcf": {
"value": null,
"timestamp": "2020-10-29T10:47:20.305Z"
}
},
"samsungvd.mediaInputSource": {
"supportedInputSourcesMap": {
"value": [
{
"id": "dtv",
"name": "TV"
},
{
"id": "HDMI1",
"name": "PlayStation 4"
},
{
"id": "HDMI4",
"name": "HT-CT370"
},
{
"id": "HDMI4",
"name": "HT-CT370"
}
],
"timestamp": "2021-10-16T15:18:11.622Z"
},
"inputSource": {
"value": "HDMI1",
"timestamp": "2021-08-28T16:29:59.716Z"
}
},
"mediaInputSource": {
"supportedInputSources": {
"value": ["digitalTv", "HDMI1", "HDMI4", "HDMI4"],
"timestamp": "2021-10-16T15:18:11.622Z"
},
"inputSource": {
"value": "HDMI1",
"timestamp": "2021-08-28T16:29:59.716Z"
}
},
"custom.tvsearch": {},
"samsungvd.ambient": {},
"refresh": {},
"custom.error": {
"error": {
"value": null,
"timestamp": "2020-08-04T21:53:22.148Z"
}
},
"execute": {
"data": {
"value": {
"payload": {
"rt": ["x.com.samsung.tv.deviceinfo"],
"if": ["oic.if.baseline", "oic.if.r"],
"x.com.samsung.country": "USA",
"x.com.samsung.infolinkversion": "T-INFOLINK2017-1008",
"x.com.samsung.modelid": "17_KANTM_UHD",
"x.com.samsung.tv.blemac": "CC:6E:A4:1F:4C:F7",
"x.com.samsung.tv.btmac": "CC:6E:A4:1F:4C:F7",
"x.com.samsung.tv.category": "tv",
"x.com.samsung.tv.countrycode": "US",
"x.com.samsung.tv.duid": "B2NBQRAG357IX",
"x.com.samsung.tv.ethmac": "c0:48:e6:e7:fc:2c",
"x.com.samsung.tv.p2pmac": "ce:6e:a4:1f:4c:f6",
"x.com.samsung.tv.udn": "717fb7ed-b310-4cfe-8954-1cd8211dd689",
"x.com.samsung.tv.wifimac": "cc:6e:a4:1f:4c:f6"
}
},
"data": {
"href": "/sec/tv/deviceinfo"
},
"timestamp": "2021-08-30T19:18:12.303Z"
}
},
"switch": {
"switch": {
"value": "on",
"timestamp": "2021-10-16T15:18:11.317Z"
}
},
"tvChannel": {
"tvChannel": {
"value": "",
"timestamp": "2020-05-07T02:58:10.479Z"
},
"tvChannelName": {
"value": "",
"timestamp": "2021-08-21T18:53:06.643Z"
}
},
"ocf": {
"st": {
"value": "2021-08-21T14:50:34Z",
"timestamp": "2021-08-21T19:19:51.890Z"
},
"mndt": {
"value": "2017-01-01",
"timestamp": "2021-08-21T19:19:51.890Z"
},
"mnfv": {
"value": "T-KTMAKUC-1290.3",
"timestamp": "2021-08-21T18:52:57.543Z"
},
"mnhw": {
"value": "0-0",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"di": {
"value": "4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"mnsl": {
"value": "http://www.samsung.com/sec/tv/overview/",
"timestamp": "2021-08-21T19:19:51.890Z"
},
"dmv": {
"value": "res.1.1.0,sh.1.1.0",
"timestamp": "2021-08-21T18:52:58.071Z"
},
"n": {
"value": "[TV] Samsung 8 Series (49)",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"mnmo": {
"value": "UN49MU8000",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"vid": {
"value": "VD-STV_2017_K",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"mnmn": {
"value": "Samsung Electronics",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"mnml": {
"value": "http://www.samsung.com",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"mnpv": {
"value": "Tizen 3.0",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"mnos": {
"value": "4.1.10",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"pi": {
"value": "4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1",
"timestamp": "2020-05-07T02:58:10.206Z"
},
"icv": {
"value": "core.1.1.0",
"timestamp": "2021-08-21T18:52:58.071Z"
}
},
"custom.picturemode": {
"pictureMode": {
"value": "Dynamic",
"timestamp": "2020-12-23T01:33:37.069Z"
},
"supportedPictureModes": {
"value": ["Dynamic", "Standard", "Natural", "Movie"],
"timestamp": "2020-05-07T02:58:10.585Z"
},
"supportedPictureModesMap": {
"value": [
{
"id": "modeDynamic",
"name": "Dynamic"
},
{
"id": "modeStandard",
"name": "Standard"
},
{
"id": "modeNatural",
"name": "Natural"
},
{
"id": "modeMovie",
"name": "Movie"
}
],
"timestamp": "2020-12-23T01:33:37.069Z"
}
},
"samsungvd.ambientContent": {
"supportedAmbientApps": {
"value": [],
"timestamp": "2021-01-17T01:10:11.985Z"
}
},
"custom.accessibility": {},
"custom.recording": {},
"custom.disabledCapabilities": {
"disabledCapabilities": {
"value": ["samsungvd.ambient", "samsungvd.ambientContent"],
"timestamp": "2021-01-17T01:10:11.985Z"
}
},
"custom.soundmode": {
"supportedSoundModesMap": {
"value": [
{
"id": "modeStandard",
"name": "Standard"
}
],
"timestamp": "2021-08-21T19:19:52.887Z"
},
"soundMode": {
"value": "Standard",
"timestamp": "2020-12-23T01:33:37.272Z"
},
"supportedSoundModes": {
"value": ["Standard"],
"timestamp": "2021-08-21T19:19:52.887Z"
}
},
"audioMute": {
"mute": {
"value": "muted",
"timestamp": "2021-08-21T19:19:52.832Z"
}
},
"mediaTrackControl": {
"supportedTrackControlCommands": {
"value": null,
"timestamp": "2020-08-04T21:53:22.384Z"
}
},
"custom.launchapp": {},
"samsungvd.firmwareVersion": {
"firmwareVersion": {
"value": null,
"timestamp": "2020-10-29T10:47:19.376Z"
}
}
}
}
}

View File

@ -0,0 +1,97 @@
{
"components": {
"main": {
"thermostatOperatingState": {
"thermostatOperatingState": {
"value": "pending cool",
"timestamp": "2025-02-10T22:04:56.341Z"
}
},
"thermostatHeatingSetpoint": {
"heatingSetpoint": {
"value": 814.7469111058201,
"unit": "F",
"timestamp": "2025-02-10T22:04:56.341Z"
},
"heatingSetpointRange": {
"value": {
"maximum": 3226.693210895862,
"step": 9234.459191378826,
"minimum": 6214.940743832475
},
"unit": "F",
"timestamp": "2025-02-10T22:04:56.341Z"
}
},
"temperatureMeasurement": {
"temperatureRange": {
"value": {
"maximum": 1826.722761785079,
"step": 138.2080712609211,
"minimum": 9268.726934158902
},
"unit": "F",
"timestamp": "2025-02-10T22:04:56.341Z"
},
"temperature": {
"value": 8554.194688973037,
"unit": "F",
"timestamp": "2025-02-10T22:04:56.341Z"
}
},
"thermostatFanMode": {
"thermostatFanMode": {
"value": "followschedule",
"data": {},
"timestamp": "2025-02-10T22:04:56.341Z"
},
"supportedThermostatFanModes": {
"value": ["on"],
"timestamp": "2025-02-10T22:04:56.341Z"
}
},
"thermostatMode": {
"thermostatMode": {
"value": "auxheatonly",
"data": {},
"timestamp": "2025-02-10T22:04:56.341Z"
},
"supportedThermostatModes": {
"value": ["rush hour"],
"timestamp": "2025-02-10T22:04:56.341Z"
}
},
"battery": {
"quantity": {
"value": 51,
"timestamp": "2025-02-10T22:04:56.341Z"
},
"battery": {
"value": 100,
"unit": "%",
"timestamp": "2025-02-10T22:04:56.341Z"
},
"type": {
"value": "38140",
"timestamp": "2025-02-10T22:04:56.341Z"
}
},
"thermostatCoolingSetpoint": {
"coolingSetpointRange": {
"value": {
"maximum": 7288.145606306409,
"step": 7620.031701049315,
"minimum": 4997.721228739137
},
"unit": "F",
"timestamp": "2025-02-10T22:04:56.341Z"
},
"coolingSetpoint": {
"value": 244.33726326608746,
"unit": "F",
"timestamp": "2025-02-10T22:04:56.341Z"
}
}
}
}
}

View File

@ -0,0 +1,13 @@
{
"components": {
"main": {
"refresh": {},
"valve": {
"valve": {
"value": "closed",
"timestamp": "2025-02-11T11:27:02.262Z"
}
}
}
}
}

View File

@ -0,0 +1,28 @@
{
"components": {
"main": {
"waterSensor": {
"water": {
"value": "dry",
"timestamp": "2025-02-10T21:58:18.784Z"
}
},
"refresh": {},
"battery": {
"quantity": {
"value": 84,
"timestamp": "2025-02-10T21:58:18.784Z"
},
"battery": {
"value": 100,
"unit": "%",
"timestamp": "2025-02-10T21:58:18.784Z"
},
"type": {
"value": "46120",
"timestamp": "2025-02-10T21:58:18.784Z"
}
}
}
}
}

View File

@ -0,0 +1,110 @@
{
"components": {
"main": {
"lock": {
"supportedUnlockDirections": {
"value": null
},
"supportedLockValues": {
"value": null
},
"lock": {
"value": "locked",
"data": {},
"timestamp": "2025-02-09T17:29:56.641Z"
},
"supportedLockCommands": {
"value": null
}
},
"refresh": {},
"battery": {
"quantity": {
"value": null
},
"battery": {
"value": 86,
"unit": "%",
"timestamp": "2025-02-09T17:18:14.150Z"
},
"type": {
"value": null
}
},
"firmwareUpdate": {
"lastUpdateStatusReason": {
"value": null
},
"availableVersion": {
"value": "00840847",
"timestamp": "2025-02-09T11:48:45.331Z"
},
"lastUpdateStatus": {
"value": null
},
"supportedCommands": {
"value": null
},
"state": {
"value": "normalOperation",
"timestamp": "2025-02-09T11:48:45.331Z"
},
"updateAvailable": {
"value": false,
"timestamp": "2025-02-09T11:48:45.332Z"
},
"currentVersion": {
"value": "00840847",
"timestamp": "2025-02-09T11:48:45.328Z"
},
"lastUpdateTime": {
"value": null
}
},
"lockCodes": {
"codeLength": {
"value": null,
"timestamp": "2020-08-04T15:29:24.127Z"
},
"maxCodes": {
"value": 250,
"timestamp": "2023-08-22T01:34:19.751Z"
},
"maxCodeLength": {
"value": 8,
"timestamp": "2023-08-22T01:34:18.690Z"
},
"codeChanged": {
"value": "8 unset",
"data": {
"codeName": "Code 8"
},
"timestamp": "2025-01-06T04:56:31.712Z"
},
"lock": {
"value": "locked",
"data": {
"method": "manual"
},
"timestamp": "2023-07-10T23:03:42.305Z"
},
"minCodeLength": {
"value": 4,
"timestamp": "2023-08-22T01:34:18.781Z"
},
"codeReport": {
"value": 5,
"timestamp": "2022-08-01T01:36:58.424Z"
},
"scanCodes": {
"value": "Complete",
"timestamp": "2025-01-06T04:56:31.730Z"
},
"lockCodes": {
"value": "{\"1\":\"Salim\",\"2\":\"Saima\",\"3\":\"Sarah\",\"4\":\"Aisha\",\"5\":\"Moiz\"}",
"timestamp": "2025-01-06T04:56:28.325Z"
}
}
}
}
}

View File

@ -0,0 +1,70 @@
{
"items": [
{
"deviceId": "f0af21a2-d5a1-437c-b10a-b34a87394b71",
"name": "aeotec-home-energy-meter-gen5",
"label": "Aeotec Energy Monitor",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "3e0921d3-0a66-3d49-b458-752e596838e9",
"deviceManufacturerCode": "0086-0002-005F",
"locationId": "6911ddf5-f0cb-4516-a06a-3a2a6ec22bca",
"ownerId": "93257fc4-6471-2566-b06e-2fe72dd979fa",
"roomId": "cdf080f0-0542-41d7-a606-aff69683e04c",
"components": [
{
"id": "main",
"label": "Meter",
"capabilities": [
{
"id": "powerMeter",
"version": 1
},
{
"id": "energyMeter",
"version": 1
},
{
"id": "voltageMeasurement",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "CurbPowerMeter",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2023-01-12T23:02:44.917Z",
"parentDeviceId": "6a2d07a4-dd77-48bc-9acf-017029aaf099",
"profile": {
"id": "6372c227-93c7-32ef-9be5-aef2221adff1"
},
"zwave": {
"networkId": "0A",
"driverId": "b98b34ce-1d1d-480c-bb17-41307a90cde0",
"executingLocally": true,
"hubId": "6a2d07a4-dd77-48bc-9acf-017029aaf099",
"networkSecurityLevel": "ZWAVE_S0_LEGACY",
"provisioningState": "PROVISIONED",
"manufacturerId": 134,
"productType": 2,
"productId": 95
},
"type": "ZWAVE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,62 @@
{
"items": [
{
"deviceId": "68e786a6-7f61-4c3a-9e13-70b803cf782b",
"name": "base-electric-meter",
"label": "Aeon Energy Monitor",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "8e619cd9-c271-3ba0-9015-62bc074bc47f",
"deviceManufacturerCode": "0086-0002-0009",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "94be4a1e-382a-4b7f-a5ef-fdb1a7d9f9e6",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "powerMeter",
"version": 1
},
{
"id": "energyMeter",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "CurbPowerMeter",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2023-06-03T16:23:57.284Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "d382796f-8ed5-3088-8735-eb03e962203b"
},
"zwave": {
"networkId": "2A",
"driverId": "4fb7ec02-2697-4d73-977d-2b1c65c4484f",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"networkSecurityLevel": "ZWAVE_LEGACY_NON_SECURE",
"provisioningState": "PROVISIONED",
"manufacturerId": 134,
"productType": 2,
"productId": 9
},
"type": "ZWAVE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,79 @@
{
"items": [
{
"deviceId": "10e06a70-ee7d-4832-85e9-a0a06a7a05bd",
"name": "c2c-arlo-pro-3-switch",
"label": "2nd Floor Hallway",
"manufacturerName": "SmartThings",
"presentationId": "SmartThings-smartthings-c2c_arlo_pro_3",
"deviceManufacturerCode": "Arlo",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "68b45114-9af8-4906-8636-b973a6faa271",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "soundSensor",
"version": 1
},
{
"id": "healthCheck",
"version": 1
},
{
"id": "videoStream",
"version": 1
},
{
"id": "motionSensor",
"version": 1
},
{
"id": "videoCapture",
"version": 1
},
{
"id": "battery",
"version": 1
},
{
"id": "alarm",
"version": 1
}
],
"categories": [
{
"name": "Camera",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2024-11-21T21:55:59.340Z",
"profile": {
"id": "89aefc3a-e210-4678-944c-638d47d296f6"
},
"viper": {
"manufacturerName": "Arlo",
"modelName": "VMC4041PB",
"endpointAppId": "viper_555d6f40-b65a-11ea-8fe0-77cb99571462"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,59 @@
{
"items": [
{
"deviceId": "571af102-15db-4030-b76b-245a691f74a5",
"name": "c2c-shade",
"label": "Curtain 1A",
"manufacturerName": "SmartThings",
"presentationId": "SmartThings-smartthings-c2c-shade",
"deviceManufacturerCode": "WonderLabs Company",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "windowShade",
"version": 1
},
{
"id": "switchLevel",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "healthCheck",
"version": 1
}
],
"categories": [
{
"name": "Blind",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-02-07T23:01:15.883Z",
"profile": {
"id": "0ceffb3e-10d3-4123-bb42-2a92c93c6e25"
},
"viper": {
"manufacturerName": "WonderLabs Company",
"modelName": "WoCurtain3",
"hwVersion": "WoCurtain3-WoCurtain3",
"endpointAppId": "viper_f18eb770-077d-11ea-bb72-9922e3ed0d38"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,67 @@
{
"items": [
{
"deviceId": "d0268a69-abfb-4c92-a646-61cec2e510ad",
"name": "plug-level-power",
"label": "Dimmer Debian",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "bb7c4cfb-6eaf-3efc-823b-06a54fc9ded9",
"deviceManufacturerCode": "CentraLite",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "94be4a1e-382a-4b7f-a5ef-fdb1a7d9f9e6",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "switchLevel",
"version": 1
},
{
"id": "powerMeter",
"version": 1
},
{
"id": "firmwareUpdate",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "SmartPlug",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2024-08-15T22:16:37.926Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "24195ea4-635c-3450-a235-71bc78ab3d1c"
},
"zigbee": {
"eui": "000D6F0003C04BC9",
"networkId": "F50E",
"driverId": "f2e891c6-00cc-446c-9192-8ebda63d9898",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"provisioningState": "PROVISIONED"
},
"type": "ZIGBEE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,71 @@
{
"items": [
{
"deviceId": "2d9a892b-1c93-45a5-84cb-0e81889498c6",
"name": "contact-profile",
"label": ".Front Door Open/Closed Sensor",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "a7f2c1d9-89b3-35a4-b217-fc68d9e4e752",
"deviceManufacturerCode": "Visonic",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "68b45114-9af8-4906-8636-b973a6faa271",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "battery",
"version": 1
},
{
"id": "firmwareUpdate",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "ContactSensor",
"categoryType": "manufacturer"
},
{
"name": "ContactSensor",
"categoryType": "user"
}
]
}
],
"createTime": "2023-09-28T17:38:59.179Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "22aa5a07-ac33-365f-b2f1-5ecef8cdb0eb"
},
"zigbee": {
"eui": "000D6F000576F604",
"networkId": "5A44",
"driverId": "408981c2-91d4-4dfc-bbfb-84ca0205d993",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"provisioningState": "PROVISIONED"
},
"type": "ZIGBEE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,311 @@
{
"items": [
{
"deviceId": "96a5ef74-5832-a84b-f1f7-ca799957065d",
"name": "[room a/c] Samsung",
"label": "AC Office Granit",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-AC-RAC-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "58d3fd7c-c512-4da3-b500-ef269382756c",
"ownerId": "f9a28d7c-1ed5-d9e9-a81c-18971ec081db",
"roomId": "85a79db4-9cf2-4f09-a5b2-cd70a5c0cef0",
"deviceTypeName": "Samsung OCF Air Conditioner",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "airConditionerMode",
"version": 1
},
{
"id": "airConditionerFanMode",
"version": 1
},
{
"id": "fanOscillationMode",
"version": 1
},
{
"id": "airQualitySensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "relativeHumidityMeasurement",
"version": 1
},
{
"id": "dustSensor",
"version": 1
},
{
"id": "veryFineDustSensor",
"version": 1
},
{
"id": "audioVolume",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "custom.spiMode",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "custom.airConditionerOptionalMode",
"version": 1
},
{
"id": "custom.airConditionerTropicalNightMode",
"version": 1
},
{
"id": "custom.autoCleaningMode",
"version": 1
},
{
"id": "custom.deviceReportStateConfiguration",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.dustFilter",
"version": 1
},
{
"id": "custom.airConditionerOdorController",
"version": 1
},
{
"id": "custom.deodorFilter",
"version": 1
},
{
"id": "custom.disabledComponents",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.dongleSoftwareInstallation",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.selfCheck",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
}
],
"categories": [
{
"name": "AirConditioner",
"categoryType": "manufacturer"
}
]
},
{
"id": "1",
"label": "1",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "airConditionerMode",
"version": 1
},
{
"id": "airConditionerFanMode",
"version": 1
},
{
"id": "fanOscillationMode",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "relativeHumidityMeasurement",
"version": 1
},
{
"id": "airQualitySensor",
"version": 1
},
{
"id": "dustSensor",
"version": 1
},
{
"id": "veryFineDustSensor",
"version": 1
},
{
"id": "odorSensor",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "audioVolume",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "custom.autoCleaningMode",
"version": 1
},
{
"id": "custom.airConditionerTropicalNightMode",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "ocf",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "custom.spiMode",
"version": 1
},
{
"id": "custom.airConditionerOptionalMode",
"version": 1
},
{
"id": "custom.deviceReportStateConfiguration",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.dustFilter",
"version": 1
},
{
"id": "custom.airConditionerOdorController",
"version": 1
},
{
"id": "custom.deodorFilter",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2021-04-06T16:43:34.753Z",
"profile": {
"id": "60fbc713-8da5-315d-b31a-6d6dcde4be7b"
},
"ocf": {
"ocfDeviceType": "oic.d.airconditioner",
"name": "[room a/c] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "ARTIK051_KRAC_18K|10193441|60010132001111110200000000000000",
"platformVersion": "0G3MPDCKA00010E",
"platformOS": "TizenRT2.0",
"hwVersion": "1.0",
"firmwareVersion": "0.1.0",
"vendorId": "DA-AC-RAC-000001",
"lastSignupTime": "2021-04-06T16:43:27.889445Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,264 @@
{
"items": [
{
"deviceId": "4ece486b-89db-f06a-d54d-748b676b4d8e",
"name": "Samsung-Room-Air-Conditioner",
"label": "Aire Dormitorio Principal",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-AC-RAC-01001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "c4189ac1-208f-461a-8ab6-ea67937b3743",
"ownerId": "85ea07e1-7063-f673-3ba5-125293f297c8",
"roomId": "1f66199a-1773-4d8f-97b7-44c312a62cf7",
"deviceTypeName": "Samsung OCF Air Conditioner",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "airConditionerMode",
"version": 1
},
{
"id": "airConditionerFanMode",
"version": 1
},
{
"id": "bypassable",
"version": 1
},
{
"id": "fanOscillationMode",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "relativeHumidityMeasurement",
"version": 1
},
{
"id": "airQualitySensor",
"version": 1
},
{
"id": "odorSensor",
"version": 1
},
{
"id": "dustSensor",
"version": 1
},
{
"id": "veryFineDustSensor",
"version": 1
},
{
"id": "audioVolume",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "audioNotification",
"version": 1
},
{
"id": "custom.spiMode",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "custom.airConditionerOptionalMode",
"version": 1
},
{
"id": "custom.airConditionerTropicalNightMode",
"version": 1
},
{
"id": "custom.autoCleaningMode",
"version": 1
},
{
"id": "custom.deviceReportStateConfiguration",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.dustFilter",
"version": 1
},
{
"id": "custom.veryFineDustFilter",
"version": 1
},
{
"id": "custom.deodorFilter",
"version": 1
},
{
"id": "custom.electricHepaFilter",
"version": 1
},
{
"id": "custom.doNotDisturbMode",
"version": 1
},
{
"id": "custom.periodicSensing",
"version": 1
},
{
"id": "custom.airConditionerOdorController",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "samsungce.alwaysOnSensing",
"version": 1
},
{
"id": "samsungce.airConditionerBeep",
"version": 1
},
{
"id": "samsungce.airConditionerLighting",
"version": 1
},
{
"id": "samsungce.airQualityHealthConcern",
"version": 1
},
{
"id": "samsungce.buttonDisplayCondition",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.dustFilterAlarm",
"version": 1
},
{
"id": "samsungce.individualControlLock",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.selfCheck",
"version": 1
},
{
"id": "samsungce.silentAction",
"version": 1
},
{
"id": "samsungce.quickControl",
"version": 1
},
{
"id": "samsungce.welcomeCooling",
"version": 1
},
{
"id": "samsungce.unavailableCapabilities",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
},
{
"id": "sec.wifiConfiguration",
"version": 1
},
{
"id": "sec.calmConnectionCare",
"version": 1
}
],
"categories": [
{
"name": "AirConditioner",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-01-28T21:31:35.755Z",
"profile": {
"id": "091a55f4-7054-39fa-b23e-b56deb7580f8"
},
"ocf": {
"ocfDeviceType": "oic.d.airconditioner",
"name": "Samsung-Room-Air-Conditioner",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "1.2.1",
"manufacturerName": "Samsung Electronics",
"modelNumber": "ARA-WW-TP1-22-COMMON|10229641|60010523001511014600083200800000",
"platformVersion": "DAWIT 2.0",
"platformOS": "TizenRT 3.1",
"hwVersion": "Realtek",
"firmwareVersion": "ARA-WW-TP1-22-COMMON_11240702",
"vendorId": "DA-AC-RAC-01001",
"vendorResourceClientServerVersion": "Realtek Release 3.1.240221",
"lastSignupTime": "2025-01-28T21:31:30.090416369Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,176 @@
{
"items": [
{
"deviceId": "2bad3237-4886-e699-1b90-4a51a3d55c8a",
"name": "Samsung Microwave",
"label": "Microwave",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-KS-MICROWAVE-0101X",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "586e4602-34ab-4a22-993e-5f616b04604f",
"ownerId": "b603d7e8-6066-4e10-8102-afa752a63816",
"roomId": "f4d03391-ab13-4c1d-b4dc-d6ddf86014a2",
"deviceTypeName": "oic.d.microwave",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "ovenSetpoint",
"version": 1
},
{
"id": "ovenMode",
"version": 1
},
{
"id": "ovenOperatingState",
"version": 1
},
{
"id": "doorControl",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.hoodFanSpeed",
"version": 1
},
{
"id": "samsungce.definedRecipe",
"version": 1
},
{
"id": "samsungce.doorState",
"version": 1
},
{
"id": "samsungce.kitchenDeviceIdentification",
"version": 1
},
{
"id": "samsungce.kitchenDeviceDefaults",
"version": 1
},
{
"id": "samsungce.ovenMode",
"version": 1
},
{
"id": "samsungce.ovenOperatingState",
"version": 1
},
{
"id": "samsungce.microwavePower",
"version": 1
},
{
"id": "samsungce.kitchenModeSpecification",
"version": 1
},
{
"id": "samsungce.kidsLock",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
}
],
"categories": [
{
"name": "Microwave",
"categoryType": "manufacturer"
}
]
},
{
"id": "hood",
"label": "hood",
"capabilities": [
{
"id": "samsungce.lamp",
"version": 1
},
{
"id": "samsungce.hoodFanSpeed",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2022-03-23T15:59:10.704Z",
"profile": {
"id": "e5db3b6f-cad6-3caa-9775-9c9cae20f4a4"
},
"ocf": {
"ocfDeviceType": "oic.d.microwave",
"name": "Samsung Microwave",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "TP2X_DA-KS-MICROWAVE-0101X|40436241|50040100011411000200000000000000",
"platformVersion": "DAWIT 3.0",
"platformOS": "TizenRT 2.0 + IPv6",
"hwVersion": "MediaTek",
"firmwareVersion": "AKS-WW-TP2-20-MICROWAVE-OTR_40230125",
"vendorId": "DA-KS-MICROWAVE-0101X",
"vendorResourceClientServerVersion": "MediaTek Release 2.220916.2",
"lastSignupTime": "2022-04-17T15:33:11.063457Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,412 @@
{
"items": [
{
"deviceId": "7db87911-7dce-1cf2-7119-b953432a2f09",
"name": "[refrigerator] Samsung",
"label": "Refrigerator",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-REF-NORMAL-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "3a1f7e7c-4e59-4c29-adb0-0813be691efd",
"deviceTypeName": "Samsung OCF Refrigerator",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "ocf",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "refrigeration",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "custom.deodorFilter",
"version": 1
},
{
"id": "custom.deviceReportStateConfiguration",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.fridgeMode",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.disabledComponents",
"version": 1
},
{
"id": "custom.waterFilter",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.dongleSoftwareInstallation",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.fridgeVacationMode",
"version": 1
},
{
"id": "samsungce.powerCool",
"version": 1
},
{
"id": "samsungce.powerFreeze",
"version": 1
},
{
"id": "samsungce.sabbathMode",
"version": 1
},
{
"id": "samsungce.quickControl",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
}
],
"categories": [
{
"name": "Refrigerator",
"categoryType": "manufacturer"
},
{
"name": "Refrigerator",
"categoryType": "user"
}
]
},
{
"id": "freezer",
"label": "freezer",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.fridgeMode",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "samsungce.freezerConvertMode",
"version": 1
},
{
"id": "samsungce.unavailableCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "cooler",
"label": "cooler",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.fridgeMode",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "samsungce.unavailableCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "cvroom",
"label": "cvroom",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.fridgeMode",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "onedoor",
"label": "onedoor",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.fridgeMode",
"version": 1
},
{
"id": "custom.thermostatSetpointControl",
"version": 1
},
{
"id": "samsungce.freezerConvertMode",
"version": 1
},
{
"id": "samsungce.unavailableCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "icemaker",
"label": "icemaker",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "icemaker-02",
"label": "icemaker-02",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "pantry-01",
"label": "pantry-01",
"capabilities": [
{
"id": "samsungce.fridgePantryInfo",
"version": 1
},
{
"id": "samsungce.fridgePantryMode",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
},
{
"id": "pantry-02",
"label": "pantry-02",
"capabilities": [
{
"id": "samsungce.fridgePantryInfo",
"version": 1
},
{
"id": "samsungce.fridgePantryMode",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2022-01-08T16:50:43.544Z",
"profile": {
"id": "f2a9af35-5df8-3477-91df-94941d302591"
},
"ocf": {
"ocfDeviceType": "oic.d.refrigerator",
"name": "[refrigerator] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "TP2X_REF_20K|00115641|0004014D011411200103000020000000",
"platformVersion": "DAWIT 2.0",
"platformOS": "TizenRT 1.0 + IPv6",
"hwVersion": "MediaTek",
"firmwareVersion": "A-RFWW-TP2-21-COMMON_20220110",
"vendorId": "DA-REF-NORMAL-000001",
"vendorResourceClientServerVersion": "MediaTek Release 2.210524.1",
"lastSignupTime": "2024-08-06T15:24:29.362093Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,119 @@
{
"items": [
{
"deviceId": "3442dfc6-17c0-a65f-dae0-4c6e01786f44",
"name": "[robot vacuum] Samsung",
"label": "Robot vacuum",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-RVC-NORMAL-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "586e4602-34ab-4a22-993e-5f616b04604f",
"ownerId": "b603d7e8-6066-4e10-8102-afa752a63816",
"roomId": "5d425f41-042a-4d9a-92c4-e43150a61bae",
"deviceTypeName": "Samsung OCF Robot Vacuum",
"components": [
{
"id": "main",
"label": "Robot vacuum",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "battery",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "robotCleanerTurboMode",
"version": 1
},
{
"id": "robotCleanerMovement",
"version": 1
},
{
"id": "robotCleanerCleaningMode",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.disabledComponents",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.robotCleanerCleaningMode",
"version": 1
},
{
"id": "samsungce.robotCleanerOperatingState",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
}
],
"categories": [
{
"name": "RobotCleaner",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2018-06-06T23:04:25Z",
"profile": {
"id": "61b1c3cd-61cc-3dde-a4ba-9477d5e559cb"
},
"ocf": {
"ocfDeviceType": "oic.d.robotcleaner",
"name": "[robot vacuum] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "powerbot_7000_17M|50016055|80010404011141000100000000000000",
"platformVersion": "00",
"platformOS": "Tizen(3/0)",
"hwVersion": "1.0",
"firmwareVersion": "1.0",
"vendorId": "DA-RVC-NORMAL-000001",
"lastSignupTime": "2020-11-03T04:43:02.729Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,168 @@
{
"items": [
{
"deviceId": "f36dc7ce-cac0-0667-dc14-a3704eb5e676",
"name": "[dishwasher] Samsung",
"label": "Dishwasher",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-WM-DW-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "586e4602-34ab-4a22-993e-5f616b04604f",
"ownerId": "b603d7e8-6066-4e10-8102-afa752a63816",
"roomId": "f4d03391-ab13-4c1d-b4dc-d6ddf86014a2",
"deviceTypeName": "Samsung OCF Dishwasher",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "execute",
"version": 1
},
{
"id": "ocf",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "dishwasherOperatingState",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.dishwasherOperatingProgress",
"version": 1
},
{
"id": "custom.dishwasherOperatingPercentage",
"version": 1
},
{
"id": "custom.dishwasherDelayStartTime",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.supportedOptions",
"version": 1
},
{
"id": "custom.waterFilter",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.dishwasherJobState",
"version": 1
},
{
"id": "samsungce.dishwasherWashingCourse",
"version": 1
},
{
"id": "samsungce.dishwasherWashingCourseDetails",
"version": 1
},
{
"id": "samsungce.dishwasherOperation",
"version": 1
},
{
"id": "samsungce.dishwasherWashingOptions",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.kidsLock",
"version": 1
},
{
"id": "samsungce.waterConsumptionReport",
"version": 1
},
{
"id": "samsungce.quickControl",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
},
{
"id": "sec.wifiConfiguration",
"version": 1
}
],
"categories": [
{
"name": "Dishwasher",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2021-06-27T01:19:35.408Z",
"profile": {
"id": "0cba797c-40ee-3473-aa01-4ee5b6cb8c67"
},
"ocf": {
"ocfDeviceType": "oic.d.dishwasher",
"name": "[dishwasher] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "DA_DW_A51_20_COMMON|30007242|40010201001311000101000000000000",
"platformVersion": "DAWIT 2.0",
"platformOS": "TizenRT 1.0 + IPv6",
"hwVersion": "ARTIK051",
"firmwareVersion": "DA_DW_A51_20_COMMON_30230714",
"vendorId": "DA-WM-DW-000001",
"vendorResourceClientServerVersion": "ARTIK051 Release 2.210224.1",
"lastSignupTime": "2021-10-16T17:28:59.984202Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,204 @@
{
"items": [
{
"deviceId": "02f7256e-8353-5bdd-547f-bd5b1647e01b",
"name": "[dryer] Samsung",
"label": "Dryer",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-WM-WD-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "781d5f1e-c87e-455e-87f7-8e954879e91d",
"ownerId": "b603d7e8-6066-4e10-8102-afa752a63816",
"roomId": "2a8637b2-77ad-475e-b537-7b6f7f97fff6",
"deviceTypeName": "Samsung OCF Dryer",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "dryerOperatingState",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.dryerDryLevel",
"version": 1
},
{
"id": "custom.dryerWrinklePrevent",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.jobBeginningStatus",
"version": 1
},
{
"id": "custom.supportedOptions",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.detergentOrder",
"version": 1
},
{
"id": "samsungce.detergentState",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.dongleSoftwareInstallation",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.dryerAutoCycleLink",
"version": 1
},
{
"id": "samsungce.dryerCycle",
"version": 1
},
{
"id": "samsungce.dryerCyclePreset",
"version": 1
},
{
"id": "samsungce.dryerDelayEnd",
"version": 1
},
{
"id": "samsungce.dryerDryingTemperature",
"version": 1
},
{
"id": "samsungce.dryerDryingTime",
"version": 1
},
{
"id": "samsungce.dryerFreezePrevent",
"version": 1
},
{
"id": "samsungce.dryerOperatingState",
"version": 1
},
{
"id": "samsungce.kidsLock",
"version": 1
},
{
"id": "samsungce.welcomeMessage",
"version": 1
},
{
"id": "samsungce.quickControl",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
},
{
"id": "sec.wifiConfiguration",
"version": 1
}
],
"categories": [
{
"name": "Dryer",
"categoryType": "manufacturer"
}
]
},
{
"id": "hca.main",
"label": "hca.main",
"capabilities": [
{
"id": "hca.dryerMode",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2021-06-01T22:54:25.907Z",
"profile": {
"id": "53a1d049-eeda-396c-8324-e33438ef57be"
},
"ocf": {
"ocfDeviceType": "oic.d.dryer",
"name": "[dryer] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "DA_WM_A51_20_COMMON|20233741|3000000100111100020B000000000000",
"platformVersion": "DAWIT 2.0",
"platformOS": "TizenRT 1.0 + IPv6",
"hwVersion": "ARTIK051",
"firmwareVersion": "DA_WM_A51_20_COMMON_30230708",
"vendorId": "DA-WM-WD-000001",
"vendorResourceClientServerVersion": "ARTIK051 Release 2.210224.1",
"lastSignupTime": "2021-06-01T22:54:22.826697Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,260 @@
{
"items": [
{
"deviceId": "f984b91d-f250-9d42-3436-33f09a422a47",
"name": "[washer] Samsung",
"label": "Washer",
"manufacturerName": "Samsung Electronics",
"presentationId": "DA-WM-WM-000001",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "781d5f1e-c87e-455e-87f7-8e954879e91d",
"ownerId": "b603d7e8-6066-4e10-8102-afa752a63816",
"roomId": "2a8637b2-77ad-475e-b537-7b6f7f97fff6",
"deviceTypeName": "Samsung OCF Washer",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "execute",
"version": 1
},
{
"id": "ocf",
"version": 1
},
{
"id": "powerConsumptionReport",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "remoteControlStatus",
"version": 1
},
{
"id": "demandResponseLoadControl",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "washerOperatingState",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "custom.dryerDryLevel",
"version": 1
},
{
"id": "custom.energyType",
"version": 1
},
{
"id": "custom.jobBeginningStatus",
"version": 1
},
{
"id": "custom.supportedOptions",
"version": 1
},
{
"id": "custom.washerAutoDetergent",
"version": 1
},
{
"id": "custom.washerAutoSoftener",
"version": 1
},
{
"id": "custom.washerRinseCycles",
"version": 1
},
{
"id": "custom.washerSoilLevel",
"version": 1
},
{
"id": "custom.washerSpinLevel",
"version": 1
},
{
"id": "custom.washerWaterTemperature",
"version": 1
},
{
"id": "samsungce.autoDispenseDetergent",
"version": 1
},
{
"id": "samsungce.autoDispenseSoftener",
"version": 1
},
{
"id": "samsungce.detergentOrder",
"version": 1
},
{
"id": "samsungce.detergentState",
"version": 1
},
{
"id": "samsungce.deviceIdentification",
"version": 1
},
{
"id": "samsungce.dongleSoftwareInstallation",
"version": 1
},
{
"id": "samsungce.detergentAutoReplenishment",
"version": 1
},
{
"id": "samsungce.softenerAutoReplenishment",
"version": 1
},
{
"id": "samsungce.driverVersion",
"version": 1
},
{
"id": "samsungce.softwareUpdate",
"version": 1
},
{
"id": "samsungce.kidsLock",
"version": 1
},
{
"id": "samsungce.softenerOrder",
"version": 1
},
{
"id": "samsungce.softenerState",
"version": 1
},
{
"id": "samsungce.washerBubbleSoak",
"version": 1
},
{
"id": "samsungce.washerCycle",
"version": 1
},
{
"id": "samsungce.washerCyclePreset",
"version": 1
},
{
"id": "samsungce.washerDelayEnd",
"version": 1
},
{
"id": "samsungce.washerFreezePrevent",
"version": 1
},
{
"id": "samsungce.washerOperatingState",
"version": 1
},
{
"id": "samsungce.washerWashingTime",
"version": 1
},
{
"id": "samsungce.washerWaterLevel",
"version": 1
},
{
"id": "samsungce.washerWaterValve",
"version": 1
},
{
"id": "samsungce.welcomeMessage",
"version": 1
},
{
"id": "samsungce.waterConsumptionReport",
"version": 1
},
{
"id": "samsungce.quickControl",
"version": 1
},
{
"id": "samsungce.energyPlanner",
"version": 1
},
{
"id": "sec.diagnosticsInformation",
"version": 1
},
{
"id": "sec.wifiConfiguration",
"version": 1
}
],
"categories": [
{
"name": "Washer",
"categoryType": "manufacturer"
}
]
},
{
"id": "hca.main",
"label": "hca.main",
"capabilities": [
{
"id": "hca.washerMode",
"version": 1
}
],
"categories": [
{
"name": "Other",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2021-06-01T22:52:18.023Z",
"profile": {
"id": "3f221c79-d81c-315f-8e8b-b5742802a1e3"
},
"ocf": {
"ocfDeviceType": "oic.d.washer",
"name": "[washer] Samsung",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "DA_WM_TP2_20_COMMON|20233741|2001000100131100022B010000000000",
"platformVersion": "DAWIT 2.0",
"platformOS": "TizenRT 2.0 + IPv6",
"hwVersion": "MediaTek",
"firmwareVersion": "DA_WM_TP2_20_COMMON_30230804",
"vendorId": "DA-WM-WM-000001",
"vendorResourceClientServerVersion": "MediaTek Release 2.211214.1",
"lastSignupTime": "2021-06-01T22:52:13.923649Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,64 @@
{
"items": [
{
"deviceId": "d5dc3299-c266-41c7-bd08-f540aea54b89",
"name": "ecobee Sensor",
"label": "Child Bedroom",
"manufacturerName": "0A0b",
"presentationId": "ST_635a866e-a3ea-4184-9d60-9c72ea603dfd",
"deviceManufacturerCode": "ecobee",
"locationId": "b6fe1fcb-e82b-4ce8-a5e1-85e96adba06c",
"ownerId": "b473ee01-2b1f-7bb1-c433-3caec75960bc",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "motionSensor",
"version": 1
},
{
"id": "presenceSensor",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "healthCheck",
"version": 1
}
],
"categories": [
{
"name": "MotionSensor",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-01-16T21:14:07.283Z",
"profile": {
"id": "8ab3ca07-0d07-471b-a276-065e46d7aa8a"
},
"viper": {
"manufacturerName": "ecobee",
"modelName": "aresSmart-ecobee3_remote_sensor",
"swVersion": "250206213001",
"hwVersion": "250206213001",
"endpointAppId": "viper_92ccdcc0-4184-11eb-b9c5-036180216747"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,80 @@
{
"items": [
{
"deviceId": "028469cb-6e89-4f14-8d9a-bfbca5e0fbfc",
"name": "v4 - ecobee Thermostat - Heat and Cool (F)",
"label": "Main Floor",
"manufacturerName": "0A0b",
"presentationId": "ST_5334da38-8076-4b40-9f6c-ac3fccaa5d24",
"deviceManufacturerCode": "ecobee",
"locationId": "b6fe1fcb-e82b-4ce8-a5e1-85e96adba06c",
"ownerId": "b473ee01-2b1f-7bb1-c433-3caec75960bc",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "relativeHumidityMeasurement",
"version": 1
},
{
"id": "thermostatHeatingSetpoint",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "thermostatOperatingState",
"version": 1
},
{
"id": "thermostatMode",
"version": 1
},
{
"id": "thermostatFanMode",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "healthCheck",
"version": 1
}
],
"categories": [
{
"name": "Thermostat",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-01-16T21:14:07.276Z",
"profile": {
"id": "234d537d-d388-497f-b0f4-2e25025119ba"
},
"viper": {
"manufacturerName": "ecobee",
"modelName": "aresSmart-thermostat",
"swVersion": "250206151734",
"hwVersion": "250206151734",
"endpointAppId": "viper_92ccdcc0-4184-11eb-b9c5-036180216747"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,50 @@
{
"items": [
{
"deviceId": "f1af21a2-d5a1-437c-b10a-b34a87394b71",
"name": "fake-fan",
"label": "Fake fan",
"manufacturerName": "Myself",
"presentationId": "3f0921d3-0a66-3d49-b458-752e596838e9",
"deviceManufacturerCode": "0086-0002-005F",
"locationId": "6f11ddf5-f0cb-4516-a06a-3a2a6ec22bca",
"ownerId": "9f257fc4-6471-2566-b06e-2fe72dd979fa",
"roomId": "cdf080f0-0542-41d7-a606-aff69683e04c",
"components": [
{
"id": "main",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "fanSpeed",
"version": 1
},
{
"id": "airConditionerFanMode",
"version": 1
}
],
"categories": [
{
"name": "Fan",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2023-01-12T23:02:44.917Z",
"parentDeviceId": "6a2dd7a4-dd77-48bc-9acf-017029aaf099",
"profile": {
"id": "6372cd27-93c7-32ef-9be5-aef2221adff1"
},
"type": "ZWAVE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,65 @@
{
"items": [
{
"deviceId": "aaedaf28-2ae0-4c1d-b57e-87f6a420c298",
"name": "GE Dimmer Switch",
"label": "Basement Exit Light",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "31cf01ee-cb49-3d95-ac2d-2afab47f25c7",
"deviceManufacturerCode": "0063-4944-3130",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"roomId": "e73dcd00-6953-431d-ae79-73fd2f2c528e",
"components": [
{
"id": "main",
"label": "Basement Exit Light",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "switchLevel",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "Switch",
"categoryType": "manufacturer"
},
{
"name": "Switch",
"categoryType": "user"
}
]
}
],
"createTime": "2020-05-25T18:18:01Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "ec5458c2-c011-3479-a59b-82b42820c2f7"
},
"zwave": {
"networkId": "14",
"driverId": "2cbf55e3-dbc2-48a2-8be5-4c3ce756b692",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"networkSecurityLevel": "ZWAVE_LEGACY_NON_SECURE",
"provisioningState": "NONFUNCTIONAL",
"manufacturerId": 99,
"productType": 18756,
"productId": 12592
},
"type": "ZWAVE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,73 @@
{
"items": [
{
"deviceId": "440063de-a200-40b5-8a6b-f3399eaa0370",
"name": "hue-color-temperature-bulb",
"label": "Bathroom spot",
"manufacturerName": "0A2r",
"presentationId": "ST_b93bec0e-1a81-4471-83fc-4dddca504acd",
"deviceManufacturerCode": "Signify Netherlands B.V.",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "colorTemperature",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "switchLevel",
"version": 1
},
{
"id": "healthCheck",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "synthetic.lightingEffectCircadian",
"version": 1
},
{
"id": "synthetic.lightingEffectFade",
"version": 1
}
],
"categories": [
{
"name": "Light",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2023-12-17T18:11:41.453Z",
"profile": {
"id": "a79e4507-ecaa-3c7e-b660-a3a71f30eafb"
},
"viper": {
"uniqueIdentifier": "ea409b82a6184ad9b49bd6318692cc1c",
"manufacturerName": "Signify Netherlands B.V.",
"modelName": "Hue ambiance spot",
"swVersion": "1.122.2",
"hwVersion": "LTG002",
"endpointAppId": "viper_71ee45b0-a794-11e9-86b2-fdd6b9f75ce6"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,81 @@
{
"items": [
{
"deviceId": "cb958955-b015-498c-9e62-fc0c51abd054",
"name": "hue-rgbw-color-bulb",
"label": "Standing light",
"manufacturerName": "0A2r",
"presentationId": "ST_2733b8dc-4b0f-4593-8e49-2432202abd52",
"deviceManufacturerCode": "Signify Netherlands B.V.",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "colorControl",
"version": 1
},
{
"id": "colorTemperature",
"version": 1
},
{
"id": "switchLevel",
"version": 1
},
{
"id": "healthCheck",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "samsungim.hueSyncMode",
"version": 1
},
{
"id": "synthetic.lightingEffectCircadian",
"version": 1
},
{
"id": "synthetic.lightingEffectFade",
"version": 1
}
],
"categories": [
{
"name": "Light",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2023-12-17T18:11:41.454Z",
"profile": {
"id": "71be1b96-c5b5-38f7-a22c-65f5392ce7ed"
},
"viper": {
"uniqueIdentifier": "f5f891a57b9d45408230b4228bdc2111",
"manufacturerName": "Signify Netherlands B.V.",
"modelName": "Hue color lamp",
"swVersion": "1.122.2",
"hwVersion": "LCA001",
"endpointAppId": "viper_71ee45b0-a794-11e9-86b2-fdd6b9f75ce6"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,41 @@
{
"items": [
{
"deviceId": "184c67cc-69e2-44b6-8f73-55c963068ad9",
"name": "iPhone",
"label": "iPhone",
"manufacturerName": "SmartThings",
"presentationId": "SmartThings-smartthings-Mobile_Presence",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "presenceSensor",
"version": 1
}
],
"categories": [
{
"name": "MobilePresence",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2021-12-02T16:14:24.394Z",
"parentDeviceId": "b8e11599-5297-4574-8e62-885995fcaa20",
"profile": {
"id": "21d0f660-98b4-3f7b-8114-fe62e555628e"
},
"type": "MOBILE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,78 @@
{
"items": [
{
"deviceId": "7d246592-93db-4d72-a10d-5a51793ece8c",
"name": "Multipurpose Sensor",
"label": "Deck Door",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "c385e2bc-acb8-317b-be2a-6efd1f879720",
"deviceManufacturerCode": "SmartThings",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"roomId": "b277a3c0-b8fe-44de-9133-c1108747810c",
"components": [
{
"id": "main",
"label": "Deck Door",
"capabilities": [
{
"id": "contactSensor",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "threeAxis",
"version": 1
},
{
"id": "accelerationSensor",
"version": 1
},
{
"id": "battery",
"version": 1
},
{
"id": "firmwareUpdate",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "MultiFunctionalSensor",
"categoryType": "manufacturer"
},
{
"name": "Door",
"categoryType": "user"
}
]
}
],
"createTime": "2019-02-23T16:53:57Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "4471213f-121b-38fd-b022-51df37ac1d4c"
},
"zigbee": {
"eui": "24FD5B00010AED6B",
"networkId": "C972",
"driverId": "408981c2-91d4-4dfc-bbfb-84ca0205d993",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"provisioningState": "PROVISIONED"
},
"type": "ZIGBEE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,64 @@
{
"items": [
{
"deviceId": "bf4b1167-48a3-4af7-9186-0900a678ffa5",
"name": "sensibo-airconditioner-1",
"label": "Office",
"manufacturerName": "0ABU",
"presentationId": "sensibo-airconditioner-1",
"deviceManufacturerCode": "Sensibo",
"locationId": "fe14085e-bacb-4997-bc0c-df08204eaea2",
"ownerId": "49228038-22ca-1c78-d7ab-b774b4569480",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "airConditionerMode",
"version": 1
},
{
"id": "healthCheck",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "AirConditioner",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2024-12-04T10:10:02.873Z",
"profile": {
"id": "ddaffb28-8ebb-4bd6-9d6f-57c28dcb434d"
},
"viper": {
"manufacturerName": "Sensibo",
"modelName": "skyplus",
"swVersion": "SKY40147",
"hwVersion": "SKY40147",
"endpointAppId": "viper_5661d200-806e-11e9-abe0-3b2f83c8954c"
},
"type": "VIPER",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,59 @@
{
"items": [
{
"deviceId": "550a1c72-65a0-4d55-b97b-75168e055398",
"name": "SYLVANIA SMART+ Smart Plug",
"label": "Arlo Beta Basestation",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "28127039-043b-3df0-adf2-7541403dc4c1",
"deviceManufacturerCode": "LEDVANCE",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "94be4a1e-382a-4b7f-a5ef-fdb1a7d9f9e6",
"components": [
{
"id": "main",
"label": "Pi Hole",
"capabilities": [
{
"id": "switch",
"version": 1
},
{
"id": "firmwareUpdate",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "Switch",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2018-10-05T12:23:14Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "daeff874-075a-32e3-8b11-bdb99d8e67c7"
},
"zigbee": {
"eui": "F0D1B80000051E05",
"networkId": "801E",
"driverId": "f2e891c6-00cc-446c-9192-8ebda63d9898",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"provisioningState": "PROVISIONED"
},
"type": "ZIGBEE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,82 @@
{
"items": [
{
"deviceId": "c85fced9-c474-4a47-93c2-037cc7829536",
"name": "sonos-player",
"label": "Elliots Rum",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "ef0a871d-9ed1-377d-8746-0da1dfd50598",
"deviceManufacturerCode": "Sonos",
"locationId": "eed0e167-e793-459b-80cb-a0b02e2b86c2",
"ownerId": "2c69cc36-85ae-c41a-9981-a4ee96cd9137",
"roomId": "105e6d1a-52a4-4797-a235-5a48d7d433c8",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "mediaPlayback",
"version": 1
},
{
"id": "mediaGroup",
"version": 1
},
{
"id": "mediaPresets",
"version": 1
},
{
"id": "mediaTrackControl",
"version": 1
},
{
"id": "audioMute",
"version": 1
},
{
"id": "audioNotification",
"version": 1
},
{
"id": "audioTrackData",
"version": 1
},
{
"id": "audioVolume",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "Speaker",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-02-02T13:18:28.570Z",
"parentDeviceId": "2f7f7d2b-e683-48ae-86f7-e57df6a0bce2",
"profile": {
"id": "0443d359-3f76-383f-82a4-6fc4a879ef1d"
},
"lan": {
"networkId": "38420B9108F6",
"driverId": "c21a6c77-872c-474e-be5b-5f6f11a240ef",
"executingLocally": true,
"hubId": "2f7f7d2b-e683-48ae-86f7-e57df6a0bce2",
"provisioningState": "TYPED"
},
"type": "LAN",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,109 @@
{
"items": [
{
"deviceId": "0d94e5db-8501-2355-eb4f-214163702cac",
"name": "Soundbar",
"label": "Soundbar Living",
"manufacturerName": "Samsung Electronics",
"presentationId": "VD-NetworkAudio-002S",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "c4189ac1-208f-461a-8ab6-ea67937b3743",
"ownerId": "85ea07e1-7063-f673-3ba5-125293f297c8",
"roomId": "db506ec3-83b1-4125-9c4c-eb597da5db6a",
"deviceTypeName": "Samsung OCF Network Audio Player",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "audioVolume",
"version": 1
},
{
"id": "audioMute",
"version": 1
},
{
"id": "audioTrackData",
"version": 1
},
{
"id": "samsungvd.audioInputSource",
"version": 1
},
{
"id": "mediaPlayback",
"version": 1
},
{
"id": "audioNotification",
"version": 1
},
{
"id": "samsungvd.soundFrom",
"version": 1
},
{
"id": "samsungvd.thingStatus",
"version": 1
},
{
"id": "samsungvd.audioGroupInfo",
"version": 1,
"ephemeral": true
}
],
"categories": [
{
"name": "NetworkAudio",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2024-10-26T02:58:40.549Z",
"profile": {
"id": "3a714028-20ea-3feb-9891-46092132c737"
},
"ocf": {
"ocfDeviceType": "oic.d.networkaudio",
"name": "Soundbar Living",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "HW-Q990C",
"platformVersion": "7.0",
"platformOS": "Tizen",
"hwVersion": "",
"firmwareVersion": "SAT-iMX8M23WWC-1010.5",
"vendorId": "VD-NetworkAudio-002S",
"vendorResourceClientServerVersion": "3.2.41",
"lastSignupTime": "2024-10-26T02:58:36.491256384Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,148 @@
{
"items": [
{
"deviceId": "4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1",
"name": "[TV] Samsung 8 Series (49)",
"label": "[TV] Samsung 8 Series (49)",
"manufacturerName": "Samsung Electronics",
"presentationId": "VD-STV_2017_K",
"deviceManufacturerCode": "Samsung Electronics",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "94be4a1e-382a-4b7f-a5ef-fdb1a7d9f9e6",
"deviceTypeName": "Samsung OCF TV",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "ocf",
"version": 1
},
{
"id": "switch",
"version": 1
},
{
"id": "audioVolume",
"version": 1
},
{
"id": "audioMute",
"version": 1
},
{
"id": "tvChannel",
"version": 1
},
{
"id": "mediaInputSource",
"version": 1
},
{
"id": "mediaPlayback",
"version": 1
},
{
"id": "mediaTrackControl",
"version": 1
},
{
"id": "custom.error",
"version": 1
},
{
"id": "custom.picturemode",
"version": 1
},
{
"id": "custom.soundmode",
"version": 1
},
{
"id": "custom.accessibility",
"version": 1
},
{
"id": "custom.launchapp",
"version": 1
},
{
"id": "custom.recording",
"version": 1
},
{
"id": "custom.tvsearch",
"version": 1
},
{
"id": "custom.disabledCapabilities",
"version": 1
},
{
"id": "samsungvd.ambient",
"version": 1
},
{
"id": "samsungvd.ambientContent",
"version": 1
},
{
"id": "samsungvd.mediaInputSource",
"version": 1
},
{
"id": "refresh",
"version": 1
},
{
"id": "execute",
"version": 1
},
{
"id": "samsungvd.firmwareVersion",
"version": 1
},
{
"id": "samsungvd.supportsPowerOnByOcf",
"version": 1
}
],
"categories": [
{
"name": "Television",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2020-05-07T02:58:10Z",
"profile": {
"id": "bac5c673-8eea-3d00-b1d2-283b46539017"
},
"ocf": {
"ocfDeviceType": "oic.d.tv",
"name": "[TV] Samsung 8 Series (49)",
"specVersion": "core.1.1.0",
"verticalDomainSpecVersion": "res.1.1.0,sh.1.1.0",
"manufacturerName": "Samsung Electronics",
"modelNumber": "UN49MU8000",
"platformVersion": "Tizen 3.0",
"platformOS": "4.1.10",
"hwVersion": "0-0",
"firmwareVersion": "T-KTMAKUC-1290.3",
"vendorId": "VD-STV_2017_K",
"locale": "en_US",
"lastSignupTime": "2021-08-21T18:52:56.748359Z",
"transferCandidate": false,
"additionalAuthCodeRequired": false
},
"type": "OCF",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,69 @@
{
"items": [
{
"deviceId": "2894dc93-0f11-49cc-8a81-3a684cebebf6",
"name": "asd",
"label": "asd",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "78906115-bf23-3c43-9cd6-f42ca3d5517a",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"roomId": "58826afc-9f38-426a-b868-dc94776286e3",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "thermostatHeatingSetpoint",
"version": 1
},
{
"id": "thermostatCoolingSetpoint",
"version": 1
},
{
"id": "thermostatOperatingState",
"version": 1
},
{
"id": "temperatureMeasurement",
"version": 1
},
{
"id": "thermostatMode",
"version": 1
},
{
"id": "thermostatFanMode",
"version": 1
},
{
"id": "battery",
"version": 1
}
],
"categories": [
{
"name": "Thermostat",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-02-10T22:04:56.174Z",
"profile": {
"id": "e921d7f2-5851-363d-89d5-5e83f5ab44c6"
},
"virtual": {
"name": "asd",
"executingLocally": false
},
"type": "VIRTUAL",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,49 @@
{
"items": [
{
"deviceId": "612ab3c2-3bb0-48f7-b2c0-15b169cb2fc3",
"name": "volvo",
"label": "volvo",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "916408b6-c94e-38b8-9fbf-03c8a48af5c3",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"roomId": "58826afc-9f38-426a-b868-dc94776286e3",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "valve",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "WaterValve",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-02-11T11:27:02.052Z",
"profile": {
"id": "f8e25992-7f5d-31da-b04d-497012590113"
},
"virtual": {
"name": "volvo",
"executingLocally": false
},
"type": "VIRTUAL",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,53 @@
{
"items": [
{
"deviceId": "a2a6018b-2663-4727-9d1d-8f56953b5116",
"name": "asd",
"label": "asd",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "838ae989-b832-3610-968c-2940491600f6",
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"ownerId": "12d4af93-cb68-b108-87f5-625437d7371f",
"roomId": "58826afc-9f38-426a-b868-dc94776286e3",
"components": [
{
"id": "main",
"label": "main",
"capabilities": [
{
"id": "waterSensor",
"version": 1
},
{
"id": "battery",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "LeakSensor",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2025-02-10T21:58:18.688Z",
"profile": {
"id": "39230a95-d42d-34d4-a33c-f79573495a30"
},
"virtual": {
"name": "asd",
"executingLocally": false
},
"type": "VIRTUAL",
"restrictionTier": 0,
"allowed": [],
"executionContext": "CLOUD"
}
],
"_links": {}
}

View File

@ -0,0 +1,67 @@
{
"items": [
{
"deviceId": "a9f587c5-5d8b-4273-8907-e7f609af5158",
"name": "Yale Push Button Deadbolt Lock",
"label": "Basement Door Lock",
"manufacturerName": "SmartThingsCommunity",
"presentationId": "45f9424f-4e20-34b0-abb6-5f26b189acb0",
"deviceManufacturerCode": "Yale",
"locationId": "c4d3b2a1-09f8-765e-4d3c-2b1a09f8e7d6 ",
"ownerId": "d47f2b19-3a6e-4c8d-bf21-9e8a7c5d134e",
"roomId": "94be4a1e-382a-4b7f-a5ef-fdb1a7d9f9e6",
"components": [
{
"id": "main",
"label": "Basement Door Lock",
"capabilities": [
{
"id": "lock",
"version": 1
},
{
"id": "lockCodes",
"version": 1
},
{
"id": "battery",
"version": 1
},
{
"id": "firmwareUpdate",
"version": 1
},
{
"id": "refresh",
"version": 1
}
],
"categories": [
{
"name": "SmartLock",
"categoryType": "manufacturer"
}
]
}
],
"createTime": "2016-11-18T23:01:19Z",
"parentDeviceId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"profile": {
"id": "51b76691-3c3a-3fce-8c7c-4f9d50e5885a"
},
"zigbee": {
"eui": "000D6F0002FB6E24",
"networkId": "C771",
"driverId": "ce930ffd-8155-4dca-aaa9-6c4158fc4278",
"executingLocally": true,
"hubId": "074fa784-8be8-4c70-8e22-6f5ed6f81b7e",
"provisioningState": "PROVISIONED"
},
"type": "ZIGBEE",
"restrictionTier": 0,
"allowed": [],
"executionContext": "LOCAL"
}
],
"_links": {}
}

View File

@ -0,0 +1,9 @@
{
"items": [
{
"locationId": "397678e5-9995-4a39-9d9f-ae6ba310236c",
"name": "Home"
}
],
"_links": null
}

View File

@ -0,0 +1,34 @@
{
"items": [
{
"sceneId": "743b0f37-89b8-476c-aedf-eea8ad8cd29d",
"sceneName": "Away",
"sceneIcon": "203",
"sceneColor": null,
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"createdBy": "12d4af93-cb68-b108-87f5-625437d7371f",
"createdDate": 1738964737000,
"lastUpdatedDate": 1738964737000,
"lastExecutedDate": null,
"editable": false,
"apiVersion": "20200501"
},
{
"sceneId": "f3341e8b-9b32-4509-af2e-4f7c952e98ba",
"sceneName": "Home",
"sceneIcon": "204",
"sceneColor": null,
"locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f",
"createdBy": "12d4af93-cb68-b108-87f5-625437d7371f",
"createdDate": 1738964731000,
"lastUpdatedDate": 1738964731000,
"lastExecutedDate": null,
"editable": false,
"apiVersion": "20200501"
}
],
"_links": {
"next": null,
"previous": null
}
}

View File

@ -0,0 +1,529 @@
# serializer version: 1
# name: test_all_entities[c2c_arlo_pro_3_switch][binary_sensor.2nd_floor_hallway_motion-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.2nd_floor_hallway_motion',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.MOTION: 'motion'>,
'original_icon': None,
'original_name': '2nd Floor Hallway motion',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '10e06a70-ee7d-4832-85e9-a0a06a7a05bd.motion',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[c2c_arlo_pro_3_switch][binary_sensor.2nd_floor_hallway_motion-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'motion',
'friendly_name': '2nd Floor Hallway motion',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.2nd_floor_hallway_motion',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[c2c_arlo_pro_3_switch][binary_sensor.2nd_floor_hallway_sound-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.2nd_floor_hallway_sound',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.SOUND: 'sound'>,
'original_icon': None,
'original_name': '2nd Floor Hallway sound',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '10e06a70-ee7d-4832-85e9-a0a06a7a05bd.sound',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[c2c_arlo_pro_3_switch][binary_sensor.2nd_floor_hallway_sound-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'sound',
'friendly_name': '2nd Floor Hallway sound',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.2nd_floor_hallway_sound',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[contact_sensor][binary_sensor.front_door_open_closed_sensor_contact-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.front_door_open_closed_sensor_contact',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.DOOR: 'door'>,
'original_icon': None,
'original_name': '.Front Door Open/Closed Sensor contact',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '2d9a892b-1c93-45a5-84cb-0e81889498c6.contact',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[contact_sensor][binary_sensor.front_door_open_closed_sensor_contact-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'door',
'friendly_name': '.Front Door Open/Closed Sensor contact',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.front_door_open_closed_sensor_contact',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_ref_normal_000001][binary_sensor.refrigerator_contact-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.refrigerator_contact',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.DOOR: 'door'>,
'original_icon': None,
'original_name': 'Refrigerator contact',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '7db87911-7dce-1cf2-7119-b953432a2f09.contact',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_ref_normal_000001][binary_sensor.refrigerator_contact-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'door',
'friendly_name': 'Refrigerator contact',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.refrigerator_contact',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[ecobee_sensor][binary_sensor.child_bedroom_motion-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.child_bedroom_motion',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.MOTION: 'motion'>,
'original_icon': None,
'original_name': 'Child Bedroom motion',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'd5dc3299-c266-41c7-bd08-f540aea54b89.motion',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[ecobee_sensor][binary_sensor.child_bedroom_motion-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'motion',
'friendly_name': 'Child Bedroom motion',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.child_bedroom_motion',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[ecobee_sensor][binary_sensor.child_bedroom_presence-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.child_bedroom_presence',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.PRESENCE: 'presence'>,
'original_icon': None,
'original_name': 'Child Bedroom presence',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'd5dc3299-c266-41c7-bd08-f540aea54b89.presence',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[ecobee_sensor][binary_sensor.child_bedroom_presence-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'presence',
'friendly_name': 'Child Bedroom presence',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.child_bedroom_presence',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[iphone][binary_sensor.iphone_presence-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.iphone_presence',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.PRESENCE: 'presence'>,
'original_icon': None,
'original_name': 'iPhone presence',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '184c67cc-69e2-44b6-8f73-55c963068ad9.presence',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[iphone][binary_sensor.iphone_presence-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'presence',
'friendly_name': 'iPhone presence',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.iphone_presence',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[multipurpose_sensor][binary_sensor.deck_door_acceleration-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.deck_door_acceleration',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.MOVING: 'moving'>,
'original_icon': None,
'original_name': 'Deck Door acceleration',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '7d246592-93db-4d72-a10d-5a51793ece8c.acceleration',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[multipurpose_sensor][binary_sensor.deck_door_acceleration-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'moving',
'friendly_name': 'Deck Door acceleration',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.deck_door_acceleration',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[multipurpose_sensor][binary_sensor.deck_door_contact-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.deck_door_contact',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.DOOR: 'door'>,
'original_icon': None,
'original_name': 'Deck Door contact',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '7d246592-93db-4d72-a10d-5a51793ece8c.contact',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[multipurpose_sensor][binary_sensor.deck_door_contact-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'door',
'friendly_name': 'Deck Door contact',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.deck_door_contact',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[virtual_valve][binary_sensor.volvo_valve-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.volvo_valve',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.OPENING: 'opening'>,
'original_icon': None,
'original_name': 'volvo valve',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '612ab3c2-3bb0-48f7-b2c0-15b169cb2fc3.valve',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[virtual_valve][binary_sensor.volvo_valve-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'opening',
'friendly_name': 'volvo valve',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.volvo_valve',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[virtual_water_sensor][binary_sensor.asd_water-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.asd_water',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.MOISTURE: 'moisture'>,
'original_icon': None,
'original_name': 'asd water',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'a2a6018b-2663-4727-9d1d-8f56953b5116.water',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[virtual_water_sensor][binary_sensor.asd_water-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'moisture',
'friendly_name': 'asd water',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.asd_water',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,356 @@
# serializer version: 1
# name: test_all_entities[da_ac_rac_000001][climate.ac_office_granit-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'fan_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.DRY: 'dry'>,
<HVACMode.FAN_ONLY: 'fan_only'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35,
'min_temp': 7,
'preset_modes': list([
'windFree',
]),
'swing_modes': None,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.ac_office_granit',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'AC Office Granit',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 441>,
'translation_key': None,
'unique_id': '96a5ef74-5832-a84b-f1f7-ca799957065d',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_ac_rac_000001][climate.ac_office_granit-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 25,
'drlc_status_duration': 0,
'drlc_status_level': -1,
'drlc_status_override': False,
'drlc_status_start': '1970-01-01T00:00:00Z',
'fan_mode': 'low',
'fan_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'friendly_name': 'AC Office Granit',
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.DRY: 'dry'>,
<HVACMode.FAN_ONLY: 'fan_only'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35,
'min_temp': 7,
'preset_mode': None,
'preset_modes': list([
'windFree',
]),
'supported_features': <ClimateEntityFeature: 441>,
'swing_mode': 'off',
'swing_modes': None,
'temperature': 25,
}),
'context': <ANY>,
'entity_id': 'climate.ac_office_granit',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_ac_rac_01001][climate.aire_dormitorio_principal-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'fan_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.DRY: 'dry'>,
<HVACMode.FAN_ONLY: 'fan_only'>,
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35,
'min_temp': 7,
'preset_modes': list([
'windFree',
]),
'swing_modes': list([
'off',
'vertical',
'horizontal',
'both',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.aire_dormitorio_principal',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Aire Dormitorio Principal',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 441>,
'translation_key': None,
'unique_id': '4ece486b-89db-f06a-d54d-748b676b4d8e',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_ac_rac_01001][climate.aire_dormitorio_principal-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 27,
'drlc_status_duration': 0,
'drlc_status_level': 0,
'drlc_status_override': False,
'drlc_status_start': '1970-01-01T00:00:00Z',
'fan_mode': 'high',
'fan_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'friendly_name': 'Aire Dormitorio Principal',
'hvac_modes': list([
<HVACMode.OFF: 'off'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.DRY: 'dry'>,
<HVACMode.FAN_ONLY: 'fan_only'>,
<HVACMode.HEAT: 'heat'>,
]),
'max_temp': 35,
'min_temp': 7,
'preset_mode': None,
'preset_modes': list([
'windFree',
]),
'supported_features': <ClimateEntityFeature: 441>,
'swing_mode': 'off',
'swing_modes': list([
'off',
'vertical',
'horizontal',
'both',
]),
'temperature': 23,
}),
'context': <ANY>,
'entity_id': 'climate.aire_dormitorio_principal',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[ecobee_thermostat][climate.main_floor-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'fan_modes': list([
'on',
'auto',
]),
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
]),
'max_temp': 35.0,
'min_temp': 7.0,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.main_floor',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Main Floor',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 395>,
'translation_key': None,
'unique_id': '028469cb-6e89-4f14-8d9a-bfbca5e0fbfc',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[ecobee_thermostat][climate.main_floor-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_humidity': 32,
'current_temperature': 21.7,
'fan_mode': 'auto',
'fan_modes': list([
'on',
'auto',
]),
'friendly_name': 'Main Floor',
'hvac_action': <HVACAction.HEATING: 'heating'>,
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
<HVACMode.HEAT_COOL: 'heat_cool'>,
]),
'max_temp': 35.0,
'min_temp': 7.0,
'supported_features': <ClimateEntityFeature: 395>,
'target_temp_high': None,
'target_temp_low': None,
'temperature': 21.7,
}),
'context': <ANY>,
'entity_id': 'climate.main_floor',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'heat',
})
# ---
# name: test_all_entities[virtual_thermostat][climate.asd-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'fan_modes': list([
'on',
]),
'hvac_modes': list([
]),
'max_temp': 35.0,
'min_temp': 7.0,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.asd',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'asd',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 395>,
'translation_key': None,
'unique_id': '2894dc93-0f11-49cc-8a81-3a684cebebf6',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[virtual_thermostat][climate.asd-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 4734.6,
'fan_mode': 'followschedule',
'fan_modes': list([
'on',
]),
'friendly_name': 'asd',
'hvac_action': <HVACAction.COOLING: 'cooling'>,
'hvac_modes': list([
]),
'max_temp': 35.0,
'min_temp': 7.0,
'supported_features': <ClimateEntityFeature: 395>,
'target_temp_high': None,
'target_temp_low': None,
'temperature': None,
}),
'context': <ANY>,
'entity_id': 'climate.asd',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@ -0,0 +1,100 @@
# serializer version: 1
# name: test_all_entities[c2c_shade][cover.curtain_1a-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'cover',
'entity_category': None,
'entity_id': 'cover.curtain_1a',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <CoverDeviceClass.SHADE: 'shade'>,
'original_icon': None,
'original_name': 'Curtain 1A',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <CoverEntityFeature: 7>,
'translation_key': None,
'unique_id': '571af102-15db-4030-b76b-245a691f74a5',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[c2c_shade][cover.curtain_1a-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_position': 100,
'device_class': 'shade',
'friendly_name': 'Curtain 1A',
'supported_features': <CoverEntityFeature: 7>,
}),
'context': <ANY>,
'entity_id': 'cover.curtain_1a',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'open',
})
# ---
# name: test_all_entities[da_ks_microwave_0101x][cover.microwave-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'cover',
'entity_category': None,
'entity_id': 'cover.microwave',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <CoverDeviceClass.DOOR: 'door'>,
'original_icon': None,
'original_name': 'Microwave',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <CoverEntityFeature: 3>,
'translation_key': None,
'unique_id': '2bad3237-4886-e699-1b90-4a51a3d55c8a',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_ks_microwave_0101x][cover.microwave-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'door',
'friendly_name': 'Microwave',
'supported_features': <CoverEntityFeature: 3>,
}),
'context': <ANY>,
'entity_id': 'cover.microwave',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@ -0,0 +1,67 @@
# serializer version: 1
# name: test_all_entities[fake_fan][fan.fake_fan-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'preset_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'fan',
'entity_category': None,
'entity_id': 'fan.fake_fan',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Fake fan',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <FanEntityFeature: 57>,
'translation_key': None,
'unique_id': 'f1af21a2-d5a1-437c-b10a-b34a87394b71',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[fake_fan][fan.fake_fan-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Fake fan',
'percentage': 2000,
'percentage_step': 33.333333333333336,
'preset_mode': None,
'preset_modes': list([
'auto',
'low',
'medium',
'high',
'turbo',
]),
'supported_features': <FanEntityFeature: 57>,
}),
'context': <ANY>,
'entity_id': 'fan.fake_fan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,267 @@
# serializer version: 1
# name: test_all_entities[centralite][light.dimmer_debian-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.dimmer_debian',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Dimmer Debian',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 32>,
'translation_key': None,
'unique_id': 'd0268a69-abfb-4c92-a646-61cec2e510ad',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[centralite][light.dimmer_debian-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': None,
'color_mode': None,
'friendly_name': 'Dimmer Debian',
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
'supported_features': <LightEntityFeature: 32>,
}),
'context': <ANY>,
'entity_id': 'light.dimmer_debian',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[ge_in_wall_smart_dimmer][light.basement_exit_light-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.basement_exit_light',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Basement Exit Light',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 32>,
'translation_key': None,
'unique_id': 'aaedaf28-2ae0-4c1d-b57e-87f6a420c298',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[ge_in_wall_smart_dimmer][light.basement_exit_light-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': None,
'color_mode': None,
'friendly_name': 'Basement Exit Light',
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
'supported_features': <LightEntityFeature: 32>,
}),
'context': <ANY>,
'entity_id': 'light.basement_exit_light',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[hue_color_temperature_bulb][light.bathroom_spot-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max_color_temp_kelvin': 9000,
'max_mireds': 500,
'min_color_temp_kelvin': 2000,
'min_mireds': 111,
'supported_color_modes': list([
<ColorMode.COLOR_TEMP: 'color_temp'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.bathroom_spot',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Bathroom spot',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 32>,
'translation_key': None,
'unique_id': '440063de-a200-40b5-8a6b-f3399eaa0370',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[hue_color_temperature_bulb][light.bathroom_spot-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': 178,
'color_mode': <ColorMode.COLOR_TEMP: 'color_temp'>,
'color_temp': 333,
'color_temp_kelvin': 3000,
'friendly_name': 'Bathroom spot',
'hs_color': tuple(
27.825,
56.895,
),
'max_color_temp_kelvin': 9000,
'max_mireds': 500,
'min_color_temp_kelvin': 2000,
'min_mireds': 111,
'rgb_color': tuple(
255,
177,
110,
),
'supported_color_modes': list([
<ColorMode.COLOR_TEMP: 'color_temp'>,
]),
'supported_features': <LightEntityFeature: 32>,
'xy_color': tuple(
0.496,
0.383,
),
}),
'context': <ANY>,
'entity_id': 'light.bathroom_spot',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[hue_rgbw_color_bulb][light.standing_light-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max_color_temp_kelvin': 9000,
'max_mireds': 500,
'min_color_temp_kelvin': 2000,
'min_mireds': 111,
'supported_color_modes': list([
<ColorMode.COLOR_TEMP: 'color_temp'>,
<ColorMode.HS: 'hs'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.standing_light',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Standing light',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': <LightEntityFeature: 32>,
'translation_key': None,
'unique_id': 'cb958955-b015-498c-9e62-fc0c51abd054',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[hue_rgbw_color_bulb][light.standing_light-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': None,
'color_mode': None,
'color_temp': None,
'color_temp_kelvin': None,
'friendly_name': 'Standing light',
'hs_color': None,
'max_color_temp_kelvin': 9000,
'max_mireds': 500,
'min_color_temp_kelvin': 2000,
'min_mireds': 111,
'rgb_color': None,
'supported_color_modes': list([
<ColorMode.COLOR_TEMP: 'color_temp'>,
<ColorMode.HS: 'hs'>,
]),
'supported_features': <LightEntityFeature: 32>,
'xy_color': None,
}),
'context': <ANY>,
'entity_id': 'light.standing_light',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,50 @@
# serializer version: 1
# name: test_all_entities[yale_push_button_deadbolt_lock][lock.basement_door_lock-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'lock',
'entity_category': None,
'entity_id': 'lock.basement_door_lock',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Basement Door Lock',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'a9f587c5-5d8b-4273-8907-e7f609af5158',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[yale_push_button_deadbolt_lock][lock.basement_door_lock-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Basement Door Lock',
'lock_state': 'locked',
'supported_features': <LockEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'lock.basement_door_lock',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'locked',
})
# ---

View File

@ -0,0 +1,101 @@
# serializer version: 1
# name: test_all_entities[scene.away-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'scene',
'entity_category': None,
'entity_id': 'scene.away',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Away',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '743b0f37-89b8-476c-aedf-eea8ad8cd29d',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[scene.away-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'color': None,
'friendly_name': 'Away',
'icon': '203',
'location_id': '88a3a314-f0c8-40b4-bb44-44ba06c9c42f',
}),
'context': <ANY>,
'entity_id': 'scene.away',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_all_entities[scene.home-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'scene',
'entity_category': None,
'entity_id': 'scene.home',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Home',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'f3341e8b-9b32-4509-af2e-4f7c952e98ba',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[scene.home-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'color': None,
'friendly_name': 'Home',
'icon': '204',
'location_id': '88a3a314-f0c8-40b4-bb44-44ba06c9c42f',
}),
'context': <ANY>,
'entity_id': 'scene.home',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,471 @@
# serializer version: 1
# name: test_all_entities[c2c_arlo_pro_3_switch][switch.2nd_floor_hallway-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.2nd_floor_hallway',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': '2nd Floor Hallway',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '10e06a70-ee7d-4832-85e9-a0a06a7a05bd',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[c2c_arlo_pro_3_switch][switch.2nd_floor_hallway-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': '2nd Floor Hallway',
}),
'context': <ANY>,
'entity_id': 'switch.2nd_floor_hallway',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[da_ks_microwave_0101x][switch.microwave-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.microwave',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Microwave',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '2bad3237-4886-e699-1b90-4a51a3d55c8a',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_ks_microwave_0101x][switch.microwave-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Microwave',
}),
'context': <ANY>,
'entity_id': 'switch.microwave',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_rvc_normal_000001][switch.robot_vacuum-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.robot_vacuum',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Robot vacuum',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '3442dfc6-17c0-a65f-dae0-4c6e01786f44',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_rvc_normal_000001][switch.robot_vacuum-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Robot vacuum',
}),
'context': <ANY>,
'entity_id': 'switch.robot_vacuum',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_dw_000001][switch.dishwasher-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.dishwasher',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Dishwasher',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_dw_000001][switch.dishwasher-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dishwasher',
}),
'context': <ANY>,
'entity_id': 'switch.dishwasher',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_wd_000001][switch.dryer-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.dryer',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Dryer',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '02f7256e-8353-5bdd-547f-bd5b1647e01b',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_wd_000001][switch.dryer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Dryer',
}),
'context': <ANY>,
'entity_id': 'switch.dryer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_wm_000001][switch.washer-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.washer',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Washer',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'f984b91d-f250-9d42-3436-33f09a422a47',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_wm_wm_000001][switch.washer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Washer',
}),
'context': <ANY>,
'entity_id': 'switch.washer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[sensibo_airconditioner_1][switch.office-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.office',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Office',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'bf4b1167-48a3-4af7-9186-0900a678ffa5',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensibo_airconditioner_1][switch.office-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Office',
}),
'context': <ANY>,
'entity_id': 'switch.office',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[smart_plug][switch.arlo_beta_basestation-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.arlo_beta_basestation',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Arlo Beta Basestation',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '550a1c72-65a0-4d55-b97b-75168e055398',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[smart_plug][switch.arlo_beta_basestation-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Arlo Beta Basestation',
}),
'context': <ANY>,
'entity_id': 'switch.arlo_beta_basestation',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[vd_network_audio_002s][switch.soundbar_living-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.soundbar_living',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Soundbar Living',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '0d94e5db-8501-2355-eb4f-214163702cac',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[vd_network_audio_002s][switch.soundbar_living-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Soundbar Living',
}),
'context': <ANY>,
'entity_id': 'switch.soundbar_living',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[vd_stv_2017_k][switch.tv_samsung_8_series_49-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.tv_samsung_8_series_49',
'has_entity_name': False,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': '[TV] Samsung 8 Series (49)',
'platform': 'smartthings',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '4588d2d9-a8cf-40f4-9a0b-ed5dfbaccda1',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[vd_stv_2017_k][switch.tv_samsung_8_series_49-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': '[TV] Samsung 8 Series (49)',
}),
'context': <ANY>,
'entity_id': 'switch.tv_samsung_8_series_49',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -1,139 +1,53 @@
"""Test for the SmartThings binary_sensor platform. """Test for the SmartThings binary_sensor platform."""
The only mocking required is of the underlying SmartThings API object so from unittest.mock import AsyncMock
real HTTP calls are not initiated during testing.
"""
from pysmartthings import ATTRIBUTES, CAPABILITIES, Attribute, Capability from pysmartthings import Attribute, Capability
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.binary_sensor import ( from homeassistant.const import STATE_OFF, STATE_ON, Platform
DEVICE_CLASSES,
DOMAIN as BINARY_SENSOR_DOMAIN,
)
from homeassistant.components.smartthings import binary_sensor
from homeassistant.components.smartthings.const import DOMAIN, SIGNAL_SMARTTHINGS_UPDATE
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE, EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .conftest import setup_platform from . import setup_integration, snapshot_smartthings_entities, trigger_update
from tests.common import MockConfigEntry
async def test_mapping_integrity() -> None: async def test_all_entities(
"""Test ensures the map dicts have proper integrity."""
# Ensure every CAPABILITY_TO_ATTRIB key is in CAPABILITIES
# Ensure every CAPABILITY_TO_ATTRIB value is in ATTRIB_TO_CLASS keys
for capability, attrib in binary_sensor.CAPABILITY_TO_ATTRIB.items():
assert capability in CAPABILITIES, capability
assert attrib in ATTRIBUTES, attrib
assert attrib in binary_sensor.ATTRIB_TO_CLASS, attrib
# Ensure every ATTRIB_TO_CLASS value is in DEVICE_CLASSES
for attrib, device_class in binary_sensor.ATTRIB_TO_CLASS.items():
assert attrib in ATTRIBUTES, attrib
assert device_class in DEVICE_CLASSES, device_class
async def test_entity_state(hass: HomeAssistant, device_factory) -> None:
"""Tests the state attributes properly match the light types."""
device = device_factory(
"Motion Sensor 1", [Capability.motion_sensor], {Attribute.motion: "inactive"}
)
await setup_platform(hass, BINARY_SENSOR_DOMAIN, devices=[device])
state = hass.states.get("binary_sensor.motion_sensor_1_motion")
assert state.state == "off"
assert state.attributes[ATTR_FRIENDLY_NAME] == f"{device.label} {Attribute.motion}"
async def test_entity_and_device_attributes(
hass: HomeAssistant, hass: HomeAssistant,
device_registry: dr.DeviceRegistry, snapshot: SnapshotAssertion,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_factory,
) -> None: ) -> None:
"""Test the attributes of the entity are correct.""" """Test all entities."""
# Arrange await setup_integration(hass, mock_config_entry)
device = device_factory(
"Motion Sensor 1",
[Capability.motion_sensor],
{
Attribute.motion: "inactive",
Attribute.mnmo: "123",
Attribute.mnmn: "Generic manufacturer",
Attribute.mnhw: "v4.56",
Attribute.mnfv: "v7.89",
},
)
# Act
await setup_platform(hass, BINARY_SENSOR_DOMAIN, devices=[device])
# Assert
entry = entity_registry.async_get("binary_sensor.motion_sensor_1_motion")
assert entry
assert entry.unique_id == f"{device.device_id}.{Attribute.motion}"
entry = device_registry.async_get_device(identifiers={(DOMAIN, device.device_id)})
assert entry
assert entry.configuration_url == "https://account.smartthings.com"
assert entry.identifiers == {(DOMAIN, device.device_id)}
assert entry.name == device.label
assert entry.model == "123"
assert entry.manufacturer == "Generic manufacturer"
assert entry.hw_version == "v4.56"
assert entry.sw_version == "v7.89"
snapshot_smartthings_entities(
async def test_update_from_signal(hass: HomeAssistant, device_factory) -> None: hass, entity_registry, snapshot, Platform.BINARY_SENSOR
"""Test the binary_sensor updates when receiving a signal."""
# Arrange
device = device_factory(
"Motion Sensor 1", [Capability.motion_sensor], {Attribute.motion: "inactive"}
)
await setup_platform(hass, BINARY_SENSOR_DOMAIN, devices=[device])
device.status.apply_attribute_update(
"main", Capability.motion_sensor, Attribute.motion, "active"
)
# Act
async_dispatcher_send(hass, SIGNAL_SMARTTHINGS_UPDATE, [device.device_id])
# Assert
await hass.async_block_till_done()
state = hass.states.get("binary_sensor.motion_sensor_1_motion")
assert state is not None
assert state.state == "on"
async def test_unload_config_entry(hass: HomeAssistant, device_factory) -> None:
"""Test the binary_sensor is removed when the config entry is unloaded."""
# Arrange
device = device_factory(
"Motion Sensor 1", [Capability.motion_sensor], {Attribute.motion: "inactive"}
)
config_entry = await setup_platform(hass, BINARY_SENSOR_DOMAIN, devices=[device])
config_entry.mock_state(hass, ConfigEntryState.LOADED)
# Act
await hass.config_entries.async_forward_entry_unload(config_entry, "binary_sensor")
# Assert
assert (
hass.states.get("binary_sensor.motion_sensor_1_motion").state
== STATE_UNAVAILABLE
) )
async def test_entity_category( @pytest.mark.parametrize("device_fixture", ["da_ref_normal_000001"])
hass: HomeAssistant, entity_registry: er.EntityRegistry, device_factory async def test_state_update(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Tests the state attributes properly match the light types.""" """Test state update."""
device1 = device_factory( await setup_integration(hass, mock_config_entry)
"Motion Sensor 1", [Capability.motion_sensor], {Attribute.motion: "inactive"}
)
device2 = device_factory(
"Tamper Sensor 2", [Capability.tamper_alert], {Attribute.tamper: "inactive"}
)
await setup_platform(hass, BINARY_SENSOR_DOMAIN, devices=[device1, device2])
entry = entity_registry.async_get("binary_sensor.motion_sensor_1_motion") assert hass.states.get("binary_sensor.refrigerator_contact").state == STATE_OFF
assert entry
assert entry.entity_category is None
entry = entity_registry.async_get("binary_sensor.tamper_sensor_2_tamper") await trigger_update(
assert entry hass,
assert entry.entity_category is EntityCategory.DIAGNOSTIC devices,
"7db87911-7dce-1cf2-7119-b953432a2f09",
Capability.CONTACT_SENSOR,
Attribute.CONTACT,
"open",
)
assert hass.states.get("binary_sensor.refrigerator_contact").state == STATE_ON

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More