Skip to content

Commit

Permalink
Merge the PR27
Browse files Browse the repository at this point in the history
  • Loading branch information
gicamm committed Feb 12, 2023
2 parents 2ef7f1f + 5f97100 commit f39b295
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 17 deletions.
1 change: 1 addition & 0 deletions custom_components/comelit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def setup(hass, config):
hass.helpers.discovery.load_platform('cover', DOMAIN, {}, config)
hass.helpers.discovery.load_platform('scene', DOMAIN, {}, config)
hass.helpers.discovery.load_platform('switch', DOMAIN, {}, config)
hass.helpers.discovery.load_platform('climate', DOMAIN, {}, config)
_LOGGER.info("Comelit Hub integration started")

# Comelit Vedo
Expand Down
89 changes: 89 additions & 0 deletions custom_components/comelit/climate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import logging


from homeassistant.components.climate import ClimateEntity

from homeassistant.components.climate import HVACMode, HVACAction

from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE
)
from homeassistant.const import (
ATTR_TEMPERATURE,
TEMP_CELSIUS,
STATE_OFF,
STATE_IDLE,
STATE_ON
)

from .const import DOMAIN

from .comelit_device import ComelitDevice

_LOGGER = logging.getLogger(__name__)



def setup_platform(hass, config, add_entities, discovery_info=None):
hass.data[DOMAIN]['hub'].climate_add_entities = add_entities
_LOGGER.info("Comelit Climate Integration started")

class ComelitClimate(ComelitDevice, ClimateEntity):
def __init__(self, id, description, state_dict, hub):
ComelitDevice.__init__(self, id, "climate", description)
self._hub = hub
self._state = state_dict

@property
def hvac_mode(self):
return HVACMode.HEAT if self._state['is_heating'] else HVACMode.OFF

@property
def hvac_modes(self):
return [HVACMode.HEAT, HVACMode.OFF]

@property
def hvac_action(self):
if self._state['is_heating']:
return HVACAction.HEATING
else:
return HVACAction.IDLE if self._state['is_enabled'] else HVACAction.OFF

@property
def target_temperature(self):
return self._state['target_temperature']

@property
def temperature_unit(self):
return TEMP_CELSIUS

@property
def current_temperature(self):
return self._state['measured_temperature']

@property
def current_humidity(self):
return self._state['measured_humidity']

@property
def supported_features(self):
return SUPPORT_TARGET_TEMPERATURE

def set_temperature(self, **kwargs):
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature is not None:
self._hub.climate_set_temperature(self._id, temperature)
self._state['target_temperature'] = temperature
self.async_schedule_update_ha_state()

def set_hvac_mode(self, hvac_mode):
self._hub.climate_set_state(self._id, hvac_mode == HVACMode.HEAT)
self.async_schedule_update_ha_state()

def update(self):
pass

@property
def state(self):
state_mapping = {HVACAction.HEATING: STATE_ON, HVACAction.IDLE: STATE_IDLE, HVACAction.OFF: STATE_OFF}
return state_mapping[self.hvac_action]
1 change: 1 addition & 0 deletions custom_components/comelit/comelit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def available(self):
return self._is_available

def update_state(self, state):
#TODO optiluca: make this more general and use it for all derived classes
old = self._state
self._state = state
if old != state:
Expand Down
16 changes: 10 additions & 6 deletions custom_components/comelit/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,19 @@ def set_cover_position(self, position, **kwargs):
_LOGGER.debug(f"Trying to SET POSITION {position} cover {self.name}! _state={self._state}")
self._hub.cover_position(self._id, position)

def open_cover(self, **kwargs):
def open_cover(self, stopping=False, **kwargs):
_LOGGER.debug(f"Trying to OPEN cover {self.name}! _state={self._state}")
self._hub.cover_up(self._id)
# self._state == STATE_OPENING
if not stopping:
self._state == STATE_OPENING
self.async_schedule_update_ha_state()

def close_cover(self, **kwargs):
def close_cover(self, stopping=False, **kwargs):
_LOGGER.debug(f"Trying to CLOSE cover {self.name}! _state={self._state}")
self._hub.cover_down(self._id)
# self._state = STATE_CLOSING
if not stopping:
self._state = STATE_CLOSING
self.async_schedule_update_ha_state()

def update_state(self, state, position):
super().update_state(state)
Expand All @@ -68,6 +72,6 @@ def update_state(self, state, position):
def stop_cover(self, **kwargs):
_LOGGER.debug(f"Trying to STOP cover {self.name}! is_opening={self.is_opening}, is_closing={self.is_closing}")
if self.is_opening:
self.close_cover()
self.close_cover(stopping=True)
elif self.is_closing:
self.open_cover()
self.open_cover(stopping=True)
94 changes: 87 additions & 7 deletions custom_components/comelit/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .light import ComelitLight
from .cover import ComelitCover
from .switch import ComelitSwitch
from .climate import ComelitClimate
import logging

_LOGGER = logging.getLogger(__name__)
Expand All @@ -29,6 +30,7 @@ class RequestType:
STATUS = 0
LIGHT = 1
AUTOMATION = 1
TEMPERATURE = 1
COVER = 1
SCENARIO = 1
ANNOUNCE = 13
Expand All @@ -39,6 +41,7 @@ class RequestType:
class HubFields:
TOKEN = 'sessiontoken'
TEMPERATURE = 'temperatura'
TARGET_TEMPERATURE = 'soglia_attiva'
HUMIDITY = 'umidita'
DESCRIPTION = 'descrizione'
INSTANT_POWER = 'instant_power'
Expand Down Expand Up @@ -152,6 +155,22 @@ def cover_position(self, id, position):
def cover_down(self, id):
self.off(RequestType.COVER, id)

def climate_set_temperature(self, id, temperature):
try:
_LOGGER.info(f'Setting climate {id} to temperature {temperature}')
req = {"req_type": RequestType.TEMPERATURE, "req_sub_type": 3, "obj_id": id, "act_type": 2, "act_params": [int(temperature*10)]}
self._hub.publish(req)
except Exception as e:
_LOGGER.exception("Error setting temperature %s", e)

def climate_set_state(self, id, state):
try:
_LOGGER.info(f'Setting climate {id} to state {state}')
req = {"req_type": RequestType.TEMPERATURE, "req_sub_type": 3, "obj_id": id, "act_type": 0, "act_params": [int(state)]}
self._hub.publish(req)
except Exception as e:
_LOGGER.exception("Error setting climate state %s", e)


# Manage scenario
class SceneHub:
Expand All @@ -173,6 +192,7 @@ class ComelitHub:
def __init__(self, client_name, hub_serial, hub_host, mqtt_port, mqtt_user, mqtt_password, hub_user, hub_password, scan_interval):
"""Initialize the sensor."""
self.sensors = {}
self.climates = {}
self.lights = {}
self.covers = {}
self.scenes = {}
Expand Down Expand Up @@ -212,14 +232,23 @@ def dispatch(self, payload):
try:
req_type = payload["req_type"]

if req_type == RequestType.STATUS:
_LOGGER.debug(f"Dispatching {payload}")
else:
_LOGGER.info(f"Dispatching {payload}")

options = {
RequestType.ANNOUNCE: self.manage_announce,
RequestType.LOGIN: self.token,
RequestType.STATUS: self.status,
RequestType.PARAMETERS: self.parse_parameters,
}
options[req_type](payload)

# I'm not 100% sure what these do, and I'm not sure why I wasn't seeing errors before.
if req_type in options:
options[req_type](payload)
except Exception as e:
_LOGGER.error(f"Error dispatching {payload}")
_LOGGER.error(e)

def manage_announce(self, payload):
Expand Down Expand Up @@ -293,6 +322,41 @@ def add_or_update_sensor(self, sensor, value):
self.sensors[name].update_state(value)
_LOGGER.debug("updated the sensor %s", name)

def update_climate(self, id, description, data):
try:
assert HubClasses.TEMPERATURE in id
_LOGGER.debug("update_climate: %s has data %s", description, data)
measured_temp = format(float(data[HubFields.TEMPERATURE]), '.1f')
measured_temp = float(measured_temp) / 10

target = format(float(data[HubFields.TARGET_TEMPERATURE]), '.1f')
target = float(target) / 10

measured_humidity = float(data[HubFields.HUMIDITY])

is_enabled = int(data['auto_man']) == 2
is_heating = bool(int(data[HubFields.STATUS]))

state_dict = {'is_enabled': is_enabled,
'is_heating': is_heating,
'measured_temperature': measured_temp,
'target_temperature': target,
'measured_humidity': measured_humidity}

climate = ComelitClimate(id, description, state_dict, CommandHub(self))

name = climate.entity_name
if climate.name not in self.climates: # Add the new sensor
if hasattr(self, 'climate_add_entities'):
self.climate_add_entities([climate])
self.climates[name] = climate
_LOGGER.info("added the climate %s", name)
else:
self.climates[name].update_state(state_dict)
_LOGGER.debug("updated the climate %s", name)
except Exception as e:
_LOGGER.exception("Error updating climate %s", e)

def update_light(self, id, description, data):
try:
_LOGGER.debug("update_light: %s has data %s", description, data)
Expand All @@ -315,7 +379,7 @@ def update_light(self, id, description, data):
self.lights[id] = light
_LOGGER.info("added the light %s %s", description, light.entity_name)
else:
_LOGGER.debug("updating the light %s %s", description, light.entity_name)
_LOGGER.debug(f"updating the light {description} {light.entity_name} with state {state}")
self.lights[id].update_state(state)
except Exception as e:
_LOGGER.exception("Error updating light %s", e)
Expand Down Expand Up @@ -384,6 +448,7 @@ def update_switch(self, id, description, data):
def update_entities(self, elements):
try:
for item in elements:
_LOGGER.debug("processing item %s", item)

id = item[HubFields.ID]

Expand All @@ -401,9 +466,13 @@ def update_entities(self, elements):
except Exception:
item = item

if HubClasses.POWER_CONSUMPTION in id or HubClasses.FTV in id or HubClasses.TEMPERATURE in id: # Sensor
if HubClasses.POWER_CONSUMPTION in id or HubClasses.FTV in id:
description = item[HubFields.DESCRIPTION]
self.update_sensor(id, description, item)
elif HubClasses.TEMPERATURE in id:
description = item[HubFields.DESCRIPTION]
self.update_sensor(id, description, item)
self.update_climate(id, description, item)
elif HubClasses.LIGHT in id:
description = item[HubFields.DESCRIPTION]
self.update_light(id, description, item)
Expand All @@ -422,15 +491,24 @@ def update_entities(self, elements):
else:
continue
except Exception as e:
_LOGGER.error("Update entities error")
_LOGGER.error(e)

def status(self, payload):
try:
elements = payload["out_data"][0][HubFields.ELEMENTS]
self.update_entities(elements)
except Exception as e:
_LOGGER.error("Status error")
_LOGGER.error(e)

def update_status(hub):
try:
req = {"req_type": RequestType.STATUS, "req_sub_type": -1, "obj_id": "GEN#17#13#1", "detail_level": 1}
hub.publish(req)
except Exception as e:
_LOGGER.error("Error updating status")
_LOGGER.error(e)

# Make a request for status
class StatusUpdater (Thread):
Expand All @@ -441,19 +519,21 @@ def __init__(self, name, scan_interval, hub):
self.hub = hub

def run(self):
parameters_timer = 0
# parameters_timer = 0
_LOGGER.debug("Comelit Hub status snapshot started")
while True:
if self.hub.sessiontoken == "":
continue

# optiluca: does not do anything?
'''
if parameters_timer == 0:
{"req_type": 8, "seq_id": 5, "req_sub_type": 23, "param_type": 2, "agent_type": 0,
"sessiontoken": "1367343208"}
parameters_timer = 30
'''

req = {"req_type": RequestType.STATUS, "req_sub_type": -1, "obj_id": "GEN#17#13#1", "detail_level": 1}
self.hub.publish(req)
update_status(self.hub)
time.sleep(self._scan_interval)
parameters_timer = parameters_timer - 1
# parameters_timer = parameters_timer - 1

7 changes: 6 additions & 1 deletion custom_components/comelit/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Import the device class from the component that you want to support
from homeassistant.components.light import (
ATTR_BRIGHTNESS, PLATFORM_SCHEMA, LightEntity, COLOR_MODE_BRIGHTNESS)
from homeassistant.const import STATE_ON
from homeassistant.const import STATE_ON, STATE_OFF

from .const import DOMAIN
from .comelit_device import ComelitDevice
Expand Down Expand Up @@ -50,6 +50,11 @@ def turn_on(self, **kwargs):
if ATTR_BRIGHTNESS in kwargs:
self._brightness = kwargs[ATTR_BRIGHTNESS]
self._light.light_on(self._id, self._brightness)
self._state = STATE_ON # Immediately update the state, don't wait for the next update
self.schedule_update_ha_state()


def turn_off(self, **kwargs):
self._light.light_off(self._id)
self._state = STATE_OFF # Immediately update the state, don't wait for the next update
self.schedule_update_ha_state()
Loading

0 comments on commit f39b295

Please sign in to comment.