Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: represent terncy scene as a homeassistant swtich #25

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions terncy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from .switch import (
TerncySmartPlug,
TerncySwitch,
TerncyScene,
TerncyButton,
)
from .cover import (
Expand Down Expand Up @@ -268,7 +269,7 @@ async def add_entity_to_platform(tern, devid, device, attrs, domain):
async def update_or_create_entity_inner(svc, tern, model, version, available):
_LOGGER.info("Updating service %s, available=%s", svc, available)

profile = svc["profile"]
profile = svc["profile"] if "profile" in svc else ""
features = -1
if profile == PROFILE_ONOFF_LIGHT:
features = SUPPORT_TERNCY_ON_OFF
Expand Down Expand Up @@ -302,7 +303,9 @@ async def update_or_create_entity_inner(svc, tern, model, version, available):
features = SUPPORT_TERNCY_ON_OFF
elif profile == PROFILE_YAN_BUTTON:
features = SUPPORT_TERNCY_ON_OFF
else:
elif model == "TERNCY-SCENE":
features = SUPPORT_TERNCY_ON_OFF
else:
_LOGGER.info("unsupported profile %d", profile)
return

Expand All @@ -313,7 +316,7 @@ async def update_or_create_entity_inner(svc, tern, model, version, available):
name = svc["name"]

device = None
attrs = svc["attributes"]
attrs = svc["attributes"] if "attributes" in svc else None

disableRelay = get_attr_value(attrs, "disableRelay")
temperature = get_attr_value(attrs, "temperature")
Expand Down Expand Up @@ -367,6 +370,11 @@ async def update_or_create_entity_inner(svc, tern, model, version, available):
device = TerncyTemperatureSensor(tern, devid + DEVID_EXT_TEMP, name + "-T", model, version, features)
device.is_available = available
await add_entity_to_platform(tern, devid + DEVID_EXT_TEMP, device, attrs, PLATFORM_SENSOR)
elif model == "TERNCY-SCENE":
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to define a const for "TERNCY-SCENE"

device = TerncyScene(tern, devid, name, model, version, features)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to forge a new devid based on the id of the returned scene, and the hub's id.
Because if user pair several different hubs, each of them may return the same scene id. If we use the scene id directly, there might be ambiguation.

For instance, devid = devid + "@" + tern.dev_id

device.is_available = available
# A scene can be turned on and off so we represent it as a switch in home assistant
await add_entity_to_platform(tern, devid, device, attrs, PLATFORM_SWITCH)
else:
device = TerncyLight(tern, devid, name, model, version, features)
device.is_available = available
Expand All @@ -388,6 +396,12 @@ async def update_or_create_entity(dev, tern):
available = True

await update_or_create_entity_inner(svc, tern, model, version, available)
elif model == "TERNCY-SCENE":
# The scene itself is a service
svc = dev

available = dev["online"] if "online" in dev else False
await update_or_create_entity_inner(svc, tern, model, version, available)

else:
if "services" not in dev:
Expand All @@ -406,6 +420,9 @@ async def async_refresh_devices(hass: HomeAssistant, tern):
group_response = await tern.get_entities("devicegroup", True)
groups = group_response["rsp"].get("entities")

scene_response = await tern.get_entities("scene", True)
scenes = scene_response["rsp"].get("entities")

pdata = tern.hass_platform_data

device_registry = dr.async_get(hass)
Expand All @@ -425,6 +442,10 @@ async def async_refresh_devices(hass: HomeAssistant, tern):
if groups:
for group in groups:
await update_or_create_entity(group, tern)

if scenes:
for scene in scenes:
await update_or_create_entity(scene, tern)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
Expand Down
84 changes: 84 additions & 0 deletions terncy/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,90 @@ def get_trigger(self, id):
},
]

class TerncyScene(SwitchEntity):
"""Representation of a Terncy scene."""

def __init__(self, api, devid, name, model, version, features):
"""Initialize the scene."""
self._device_id = devid
self.hub_id = api.dev_id
self._name = name
self.model = model
self.version = version
self.api = api
self.is_available = False
self._features = features
self._onoff = False

def update_state(self, attrs):
"""Updateterncy state."""
_LOGGER.info("update state event to %s", attrs)
on_off = get_attr_value(attrs, "on")
if on_off is not None:
self._onoff = on_off == 1

@property
def unique_id(self):
"""Return terncy unique id."""
return self._device_id

@property
def device_id(self):
"""Return terncy device id."""
return self._device_id

@property
def device_class(self):
"""Return if terncy device is available."""
return DEVICE_CLASS_OUTLET

@property
def name(self):
"""Return terncy device name."""
return self._name

@property
def is_on(self):
"""Return if terncy device is on."""
return self._onoff

@property
def available(self):
"""Return if terncy device is available."""
return self.is_available

@property
def supported_features(self):
"""Return the terncy device feature."""
return self._features

@property
def device_info(self):
"""Return the terncy device info."""
return {
"identifiers": {(DOMAIN, self.device_id)},
"connections": {(dr.CONNECTION_ZIGBEE, self.device_id)},
"name": self.name,
"manufacturer": TERNCY_MANU_NAME,
"model": self.model,
"sw_version": self.version,
"via_device": (DOMAIN, self.hub_id),
}

async def async_turn_on(self, **kwargs):
"""Turn on terncy smart plug."""
_LOGGER.info("turn on %s", kwargs)
self._onoff = True
await self.api.set_onoff(self._device_id, 1)
self.async_write_ha_state()

async def async_turn_off(self, **kwargs):
"""Turn off terncy smart plug."""
_LOGGER.info("turn off")
self._onoff = False
await self.api.set_onoff(self._device_id, 0)
self.async_write_ha_state()

class TerncyButton(ButtonEntity):
"""Representation of a Terncy Stateless Button."""

Expand Down
2 changes: 2 additions & 0 deletions terncy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

def get_attr_value(attrs, key):
"""Read attr value from terncy attributes."""
if attrs is None:
return None
for att in attrs:
if "attr" in att and att["attr"] == key:
return att["value"]
Expand Down