From cc55546dff286725c474402ccba1ea3776d15fd2 Mon Sep 17 00:00:00 2001 From: Minims Date: Thu, 12 Oct 2023 22:46:15 +0200 Subject: [PATCH 1/7] feature: add light level --- myFox2Mqtt/business/__init__.py | 17 + myFox2Mqtt/docker-compose.yml | 2 +- myFox2Mqtt/homeassistant/ha_discovery.py | 7 + myFox2Mqtt/main.py | 65 +-- myFox2Mqtt/myfox/api/__init__.py | 2 +- myFox2Mqtt/myfox/websocket/__init__.py | 540 ----------------------- myFox2Mqtt/requirements.txt | 1 - 7 files changed, 44 insertions(+), 590 deletions(-) delete mode 100644 myFox2Mqtt/myfox/websocket/__init__.py diff --git a/myFox2Mqtt/business/__init__.py b/myFox2Mqtt/business/__init__.py index 6cd1673..656cbf2 100644 --- a/myFox2Mqtt/business/__init__.py +++ b/myFox2Mqtt/business/__init__.py @@ -229,6 +229,23 @@ def ha_devices_config( retain=True, ) + # Light + for light_device in light_devices: + if light_device.get("deviceId") == device.device_id: + LOGGER.info(f"Found Light for {device.device_id}: {light_device.get('level')}") + light = ha_discovery_devices( + site_id=site_id, + device=device, + mqtt_config=mqtt_config, + sensor_name="level", + ) + mqtt_publish( + mqtt_client=mqtt_client, + topic=light.get("topic"), + payload=light.get("config"), + retain=True, + ) + # Smoke for other_device in other_devices: if other_device.get("deviceId") == device.device_id: diff --git a/myFox2Mqtt/docker-compose.yml b/myFox2Mqtt/docker-compose.yml index 932a6ed..e6ec08c 100644 --- a/myFox2Mqtt/docker-compose.yml +++ b/myFox2Mqtt/docker-compose.yml @@ -2,6 +2,6 @@ version: "3" services: myfox2mqtt: build: . - image: myfox2mqtt:v2023.10.1 + image: myfox2mqtt:v2023.10.2 volumes: - ./config:/config diff --git a/myFox2Mqtt/homeassistant/ha_discovery.py b/myFox2Mqtt/homeassistant/ha_discovery.py index 294bba2..65e0a49 100644 --- a/myFox2Mqtt/homeassistant/ha_discovery.py +++ b/myFox2Mqtt/homeassistant/ha_discovery.py @@ -112,6 +112,13 @@ "unit_of_measurement": "°C", }, }, + "level": { + "type": "sensor", + "config": { + "device_class": "illuminance", + "unit_of_measurement": "lx", + }, + }, "battery_level": { "type": "sensor", "config": { diff --git a/myFox2Mqtt/main.py b/myFox2Mqtt/main.py index 8f688fd..8a3bb88 100755 --- a/myFox2Mqtt/main.py +++ b/myFox2Mqtt/main.py @@ -3,8 +3,6 @@ import argparse import logging import threading -from functools import partial -from signal import SIGINT, SIGTERM, signal import time from exceptions import MyFoxInitError @@ -13,9 +11,8 @@ from mqtt import init_mqtt from myfox.sso import init_sso from myfox.api import MyFoxApi -from myfox.websocket import MyFoxWebsocket -VERSION = "2023.10.1" +VERSION = "2023.10.2" def myfox_loop(config, mqtt_client, api): @@ -29,16 +26,6 @@ def myfox_loop(config, mqtt_client, api): close_and_exit(myfox_api, 3) -def myfox_wss_loop(sso, debug, config, mqtt_client, api): - """MyFox WSS Loop""" - try: - wss = MyFoxWebsocket(sso=sso, debug=debug, config=config, mqtt_client=mqtt_client, api=api) - wss.run_forever() - except Exception as exc: - LOGGER.error(f"Force stopping WebSocket {exc}") - close_and_exit(wss, 3) - - if __name__ == "__main__": # Read Arguments PARSER = argparse.ArgumentParser() @@ -59,12 +46,6 @@ def myfox_wss_loop(sso, debug, config, mqtt_client, api): API = MyFoxApi(sso=SSO) MQTT_CLIENT = init_mqtt(config=CONFIG, api=API) - # Trigger Ctrl-C - signal(SIGINT, partial(close_and_exit, MyFox2Mqtt, 0)) - signal(SIGTERM, partial(close_and_exit, MyFox2Mqtt, 0)) - signal(SIGINT, partial(close_and_exit, MyFoxWebsocket, 0)) - signal(SIGTERM, partial(close_and_exit, MyFoxWebsocket, 0)) - try: p1 = threading.Thread( target=myfox_loop, @@ -74,33 +55,23 @@ def myfox_wss_loop(sso, debug, config, mqtt_client, api): API, ), ) - # p2 = threading.Thread( - # target=myfox_wss_loop, - # args=( - # SSO, - # DEBUG, - # CONFIG, - # MQTT_CLIENT, - # API, - # ), - # ) + p1.start() - # p2.start() - # p2.join() - # while True: - # if not p2.is_alive(): - # p2 = threading.Thread( - # target=myfox_wss_loop, - # args=( - # SSO, - # DEBUG, - # CONFIG, - # MQTT_CLIENT, - # API, - # ), - # ) - # p2.start() - # p2.join() + + while True: + if not p1.is_alive(): + LOGGER.warning("API is DEAD, restarting") + p1 = threading.Thread( + target=myfox_loop, + args=( + CONFIG, + MQTT_CLIENT, + API, + ), + ) + p1.start() + + time.sleep(1) + except Exception as exp: LOGGER.error(f"Force stopping application {exp}") - # close_and_exit(MYFOX, 3) diff --git a/myFox2Mqtt/myfox/api/__init__.py b/myFox2Mqtt/myfox/api/__init__.py index e57213c..d128939 100644 --- a/myFox2Mqtt/myfox/api/__init__.py +++ b/myFox2Mqtt/myfox/api/__init__.py @@ -524,7 +524,7 @@ def get_device_light(self, site_id: str, device_id: str): """ response = self.get(f"/v2/site/{site_id}/device/{device_id}/data/light/") response.raise_for_status() - LOGGER.info(f"Device: {response.json()}") + LOGGER.info(f"Light: {response.json()}") return response.json() def get_devices_other( diff --git a/myFox2Mqtt/myfox/websocket/__init__.py b/myFox2Mqtt/myfox/websocket/__init__.py deleted file mode 100644 index 6547107..0000000 --- a/myFox2Mqtt/myfox/websocket/__init__.py +++ /dev/null @@ -1,540 +0,0 @@ -"""MyFox Websocket""" -import base64 -import json -import logging -import os -import ssl -import time - -from business.mqtt import mqtt_publish, update_site, update_device -from homeassistant.ha_discovery import ALARM_STATUS -from mqtt import MQTTClient, init_mqtt -from oauthlib.oauth2 import LegacyApplicationClient, TokenExpiredError -from requests_oauthlib import OAuth2Session -from myfox.api import MyFoxApi -from myfox.sso import MyFoxSso, read_token_from_file - -import websocket -from websocket import WebSocketApp - -WEBSOCKET = "wss://websocket.myfox.io/events/websocket?token=" - -LOGGER = logging.getLogger(__name__) - - -class MyFoxWebsocket: - """MyFox WebSocket Class""" - - def __init__( - self, - sso: MyFoxSso, - config: dict, - mqtt_client: MQTTClient, - api: MyFoxApi, - debug: bool = False, - ): - self.mqtt_client = mqtt_client - self.mqtt_config = config.get("mqtt") - self.api = api - self.sso = sso - - if debug: - websocket.enableTrace(True) - LOGGER.debug(f"Opening websocket connection to {WEBSOCKET}") - self.token = self.sso.request_token() - self._websocket = WebSocketApp( - f"{WEBSOCKET}{self.token.get('access_token')}", - on_message=self.on_message, - on_error=self.on_error, - on_close=self.on_close, - ) - - def run_forever(self): - """Run Forever Loop""" - self._websocket.run_forever( - ping_timeout=10, - ping_interval=30, - sslopt={"cert_reqs": ssl.CERT_NONE}, - ) - - def on_message(self, ws_app, message): - """Handle New message received on WebSocket""" - if "websocket.connection.ready" in message: - LOGGER.info("Websocket Connection is READY") - return - - if "websocket.error.token" in message: - LOGGER.warning("Websocket Token Error: requesting a new one") - self.sso.refresh_tokens() - self._websocket.close() - - logging.debug(f"Message: {message}") - - message_json = json.loads(message) - callbacks = { - "security.level.change": self.security_level_change, - "alarm.trespass": self.alarm_trespass, - "alarm.panic": self.alarm_panic, - "alarm.end": self.alarm_end, - "presence_out": self.update_keyfob_presence, - "presence_in": self.update_keyfob_presence, - "device.status": self.device_status, - } - - ack = { - "ack": True, - "message_id": message_json["message_id"], - "client": "Android", - } - ws_app.send(json.dumps(ack)) - self.default_message(message_json) - if message_json["key"] in callbacks: - callbacks[message_json["key"]](message_json) - else: - LOGGER.debug(f"Unknown message: {message}") - - def on_error(self, ws_app, message): # pylint: disable=unused-argument,no-self-use - """Handle Websocket Errors""" - LOGGER.error(f"Error in the websocket connection: {message}") - - def on_close(self, ws_app, close_status_code, close_msg): # pylint: disable=unused-argument,no-self-use - """Handle Websocket Close Connection""" - LOGGER.info("Closing websocket connection") - LOGGER.info("Reconnecting") - time.sleep(2) - self.run_forever() - - def update_keyfob_presence(self, message): - """Update Key Fob Presence""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"event", - # "key":"presence_in", - # "user_id":"XXX", - # "device_id":"XXX", - # "device_type":"fob", - # "message_id":"XXX" - # }, - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"event", - # "key":"presence_out", - # "user_id":"XXX", - # "device_id":"XXX", - # "device_type":"fob", - # "message_id":"XXX" - # } - LOGGER.info("Update Key Fob Presence") - site_id = message.get("site_id") - device_id = message.get("device_id") - LOGGER.info(message) - payload = {"presence": "unknown"} - if message.get("key") == "presence_out": - payload = {"presence": "not_home"} - if message.get("key") == "presence_in": - payload = {"presence": "home"} - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/{device_id}/presence" - - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - retain=True, - ) - - def security_level_change(self, message): - """Update Alarm Status""" - # { - # "profiles":[ - # "owner", - # "admin", - # "guest", - # "kid" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"security.level.change", - # "security_level":"armed", - # "message_id":"XXX" - # } - LOGGER.info("Update Alarm Status") - site_id = message.get("site_id") - security_level = message.get("security_level") - payload = ({"security_level": ALARM_STATUS.get(security_level, "disarmed")},) - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/state" - - mqtt_publish(mqtt_client=self.mqtt_client, topic=topic, payload=payload) - - def alarm_trespass(self, message): - """Alarm Triggered !!""" - # { - # "profiles":[ - # "owner", - # "admin", - # "custom", - # "family", - # "neighbor" - # ], - # "site_id":"XXX", - # "type":"alarm", - # "key":"alarm.trespass", - # "device_id":"XXX", - # "device_type":"pir", - # "start_at":"2022-03-14T17:17:12.000000Z", - # "start_siren_at":"2022-03-14T17:17:42.000000Z", - # "end_at":"2022-03-14T17:20:42.000000Z", - # "end_siren_at":"2022-03-14T17:20:42.000000Z", - # "manual_alarm":false, - # "message_id":"XXX" - # } - LOGGER.info("Report Alarm Triggered") - site_id = message.get("site_id") - device_id = message.get("device_id") - device_type = message.get("device_type") - security_level = "triggered" - if message.get("type") != "alarm": - LOGGER.info(f"{message.get('type')} is not 'alarm'") - payload = {"security_level": security_level} - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/state" - - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - retain=True, - ) - - if device_type == "pir": - LOGGER.info("Trigger PIR Sensor") - payload = {"motion_sensor": "True"} - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/{device_id}/pir" - - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - ) - time.sleep(3) - payload = {"motion_sensor": "False"} - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - ) - - def alarm_panic(self, message): - """Report Alarm Panic""" - # { - # "profiles":[ - # "owner", - # "admin", - # "custom", - # "family", - # "neighbor" - # ], - # "site_id":"XXX", - # "type":"alarm", - # "key":"alarm.panic", - # "device_id":null, - # "device_type":null, - # "start_at":"2022-03-14T17:21:07.000000Z", - # "start_siren_at":"2022-03-14T17:21:07.000000Z", - # "end_at":"2022-03-14T17:24:07.000000Z", - # "end_siren_at":"2022-03-14T17:24:07.000000Z", - # "manual_alarm":false, - # "message_id":"XXX" - # } - LOGGER.info("Report Alarm Panic") - site_id = message.get("site_id") - security_level = "triggered" - payload = {"security_level": security_level} - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/state" - - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - retain=True, - ) - - def alarm_end(self, message): - """Report Alarm Stop""" - # { - # "profiles":[ - # "owner", - # "admin", - # "custom", - # "family", - # "neighbor" - # ], - # "site_id":"XXX", - # "type":"alarm", - # "key":"alarm.end", - # "device_id":null, - # "device_type":null, - # "end_at":"2022-03-14T17:19:22.000000Z", - # "end_siren_at":null, - # "stopped_by_user_id":"XXX", - # "message_id":"XXX" - # } - LOGGER.info("Report Alarm Stop") - site_id = message.get("site_id") - update_site(self.api, self.mqtt_client, self.mqtt_config, site_id) - - def device_status(self, message): - """Update Device Status""" - # { - # "profiles":[ - # "admin", - # "owner", - # "installer_write" - # ], - # "site_id":"XXX", - # "type":"testing", - # "key":"device.status", - # "device_id":"XXX", - # "device_lost":false, - # "rlink_quality":-73, - # "rlink_quality_percent":75, - # "battery_level":100, - # "recalibration_required":false, - # "cover_present":true, - # "last_status_at":"2022-03-16T16:06:56.000000Z", - # "diagnosis":{ - # "is_everything_ok":true, - # "problems":[ - # ] - # }, - # "message_id":"XXX" - # } - site_id = message.get("site_id") - device_id = message.get("device_id") - LOGGER.info(f"It Seems the Door {device_id} is moving") - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/{device_id}/pir" - payload = {"motion_sensor": "True"} - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - ) - time.sleep(3) - payload = {"motion_sensor": "False"} - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=payload, - ) - - def site_device_testing_status(self, message): - """Site Device Testing Status""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"testing", - # "key":"site.device.testing.status", - # "diagnosis":{ - # "main_status":"ok", - # "main_message":"diagnosis.ok", - # "main_message_vars":{ - - # }, - # "device_diagnosis_available":true, - # "device_diagnosis_expired":false, - # "items":[ - - # ] - # }, - # "message_id":"XXX" - # } - - def default_message(self, message): - """Default Message""" - LOGGER.info(f"[default] Read Message {message}") - topic_suffix = message.get("key") - site_id = message.get("site_id") - device_id = message.get("device_id") - if not site_id: - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{topic_suffix}" - elif not device_id: - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/{topic_suffix}" - else: - topic = f"{self.mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/{device_id}/{topic_suffix}" - mqtt_publish( - mqtt_client=self.mqtt_client, - topic=topic, - payload=message, - retain=True, - ) - - def remote_unassigned(self, message): - """Remote Unassigned""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"remote_unassigned", - # "user_id":"XXX", - # "device_id":"XXX", - # "message_id":"XXX" - # } - - def device_firmware_update_fail(self, message): - """Device Firmware Update Fail""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"device.firmware.update.fail", - # "device_id":"XXX", - # "reason":100, - # "message_id":"XXX" - # } - - def site_privacy(self, message): - """Site Privacy""" - # { - # "profiles":[ - # "admin", - # "owner", - # "installer_write" - # ], - # "site_id":"XXX", - # "type":"event", - # "key":"site.privacy", - # "active":true, - # "message_id":"XXX" - # } - - def camerastatus_shutter(self, message): - """Camera Status Shutter Close""" - # { - # "profiles":[ - # "admin", - # "owner", - # "installer_write" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"camerastatus.shutter.close", - # "device_id":"XXX", - # "message_id":"XXX" - # } - # { - # "profiles":[ - # "admin", - # "owner", - # "installer_write" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"camerastatus.shutter.open", - # "device_id":"XXX", - # "message_id":"XXX" - # } - - def snapshot_ready(self, message): - """Snapshot Ready""" - # { - # "profiles":[ - # "owner" - # ], - # "site_id":"XXX", - # "key":"snapshotready", - # "snapshot_id":"XXX", - # "device_id":"XXX", - # "snapshot_url":"https:\/\/video-cdn.myfox.io\/camera_snapshot\/XXX\/XXX.XXX-s?Expires=1647629662&Signature=XXX-XXX~XXX~XXX~XXX~XXX-XXX~XXX~XXX&Key-Pair-Id=XXX", - # "message_id":"XXX", - # "type":"event" - # } - - def box_update_progress(self, message): - """Box Update Progress""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"box.update.progress", - # "box_id":"XXX", - # "progress":100, - # "remaining":0, - # "total":0, - # "update":"no update", - # "message_id":"XXX" - # } - - def device_offline(self, message): - """Device Offline""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"event", - # "key":"device.offline", - # "device_id":"XXX", - # "message_id":"XXX" - # } - - def device_update_connect(self, message): - """Device Update Connect""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"config", - # "key":"device.update.connect", - # "device_type":"mss_plug", - # "device_mac":"XXX", - # "message_id":"XXX" - # } - - def diagnosis_connection_online_camera(self, message): - """Diagnosis Connection Online Camera""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"event", - # "key":"diagnosis.connection.online.camera", - # "device_id":"XXX", - # "message_id":"XXX" - # } - - def diagnosis_connection_offline_camera(self, message): - """Diagnosis Connection Offline Camera""" - # { - # "profiles":[ - # "owner", - # "admin" - # ], - # "site_id":"XXX", - # "type":"event", - # "key":"diagnosis.connection.offline.camera", - # "device_id":"XXX", - # "message_id":"XX" - # } diff --git a/myFox2Mqtt/requirements.txt b/myFox2Mqtt/requirements.txt index 1abda41..34ded4e 100644 --- a/myFox2Mqtt/requirements.txt +++ b/myFox2Mqtt/requirements.txt @@ -4,4 +4,3 @@ paho-mqtt==1.6.1 pyyaml==6.0.1 requests-oauthlib==1.3.1 schedule==1.2.0 -websocket-client==1.6.1 \ No newline at end of file From b2f245b31b0b49d59d530bb7e49d2c6d2226379e Mon Sep 17 00:00:00 2001 From: Minims Date: Mon, 16 Oct 2023 20:52:16 +0200 Subject: [PATCH 2/7] fix: close shutter --- myFox2Mqtt/homeassistant/ha_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/myFox2Mqtt/homeassistant/ha_discovery.py b/myFox2Mqtt/homeassistant/ha_discovery.py index 65e0a49..ee91cbf 100644 --- a/myFox2Mqtt/homeassistant/ha_discovery.py +++ b/myFox2Mqtt/homeassistant/ha_discovery.py @@ -26,7 +26,7 @@ }, "shutter": { "type": "cover", - "config": {"pl_open": "open", "pl_close": "close", "pl_stop": "my", "optimistic": "true"}, + "config": {"pl_open": "open", "pl_cls": "close", "pl_stop": "my", "optimistic": "true"}, }, "socket": { "type": "switch", From c3309722790368acb4ab287883f1480f3355b56e Mon Sep 17 00:00:00 2001 From: Minims Date: Sun, 22 Oct 2023 14:51:52 +0200 Subject: [PATCH 3/7] fix: light level --- myFox2Mqtt/business/__init__.py | 4 ++-- myFox2Mqtt/homeassistant/ha_discovery.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/myFox2Mqtt/business/__init__.py b/myFox2Mqtt/business/__init__.py index 656cbf2..f3a7f26 100644 --- a/myFox2Mqtt/business/__init__.py +++ b/myFox2Mqtt/business/__init__.py @@ -232,12 +232,12 @@ def ha_devices_config( # Light for light_device in light_devices: if light_device.get("deviceId") == device.device_id: - LOGGER.info(f"Found Light for {device.device_id}: {light_device.get('level')}") + LOGGER.info(f"Found Light for {device.device_id}: {light_device.get('light')}") light = ha_discovery_devices( site_id=site_id, device=device, mqtt_config=mqtt_config, - sensor_name="level", + sensor_name="light", ) mqtt_publish( mqtt_client=mqtt_client, diff --git a/myFox2Mqtt/homeassistant/ha_discovery.py b/myFox2Mqtt/homeassistant/ha_discovery.py index ee91cbf..ad597e7 100644 --- a/myFox2Mqtt/homeassistant/ha_discovery.py +++ b/myFox2Mqtt/homeassistant/ha_discovery.py @@ -112,7 +112,7 @@ "unit_of_measurement": "°C", }, }, - "level": { + "light": { "type": "sensor", "config": { "device_class": "illuminance", From 5a30d4d3a7db4641b9391ce473a6d05d577299ac Mon Sep 17 00:00:00 2001 From: Minims Date: Sun, 22 Oct 2023 15:08:42 +0200 Subject: [PATCH 4/7] feature: add gate --- myFox2Mqtt/business/__init__.py | 19 ++++++++++ myFox2Mqtt/homeassistant/ha_discovery.py | 4 +++ myFox2Mqtt/myfox/api/__init__.py | 44 ++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/myFox2Mqtt/business/__init__.py b/myFox2Mqtt/business/__init__.py index f3a7f26..ad52e26 100644 --- a/myFox2Mqtt/business/__init__.py +++ b/myFox2Mqtt/business/__init__.py @@ -104,6 +104,7 @@ def ha_devices_config( other_devices = api.get_devices_other(site_id=site_id) camera_devices = api.get_devices_camera(site_id=site_id) shutter_devices = api.get_devices_shutter(site_id=site_id) + gate_devices = api.get_devices_gate(site_id=site_id) socket_devices = api.get_devices_socket(site_id=site_id) for device in my_devices: @@ -281,6 +282,24 @@ def ha_devices_config( ) mqtt_client.client.subscribe(shutter.get("config").get("command_topic")) + # Gate + for gate_device in gate_devices: + if gate_device.get("deviceId") == device.device_id: + LOGGER.info(f"Found Gate for {device.device_id}: {gate_device.get('label')}") + gate = ha_discovery_devices( + site_id=site_id, + device=device, + mqtt_config=mqtt_config, + sensor_name="gate", + ) + mqtt_publish( + mqtt_client=mqtt_client, + topic=gate.get("topic"), + payload=gate.get("config"), + retain=True, + ) + mqtt_client.client.subscribe(gate.get("config").get("command_topic")) + # Sockets for socket_device in socket_devices: if socket_device.get("deviceId") == device.device_id: diff --git a/myFox2Mqtt/homeassistant/ha_discovery.py b/myFox2Mqtt/homeassistant/ha_discovery.py index ad597e7..7c4213f 100644 --- a/myFox2Mqtt/homeassistant/ha_discovery.py +++ b/myFox2Mqtt/homeassistant/ha_discovery.py @@ -28,6 +28,10 @@ "type": "cover", "config": {"pl_open": "open", "pl_cls": "close", "pl_stop": "my", "optimistic": "true"}, }, + "gate": { + "type": "cover", + "config": {"pl_open": "one", "pl_cls": "two", "optimistic": "true", "device_class": "gate"}, + }, "socket": { "type": "switch", "config": { diff --git a/myFox2Mqtt/myfox/api/__init__.py b/myFox2Mqtt/myfox/api/__init__.py index d128939..6740439 100644 --- a/myFox2Mqtt/myfox/api/__init__.py +++ b/myFox2Mqtt/myfox/api/__init__.py @@ -630,6 +630,50 @@ def shutter_action_device( response.raise_for_status() return response.json() + def get_devices_gate(self, site_id: str): + """Get Devices Gate from a Site ID + + Args: + site_id (Optional[str], optional): Site ID. Defaults to None. + + Returns: + List[Device]: List of Device object + """ + response = self.get(f"/v2/site/{site_id}/device/gate/items") + try: + content = response.json() + except JSONDecodeError: + response.raise_for_status() + LOGGER.info(f"Devices Gate: {response.json()}") + return content.get("payload").get("items") + + def gate_action_device( + self, + site_id: str, + device_id: str, + action: str, + ) -> Dict: + """Make an action on a Gate + + Args: + site_id (str): Site ID + device_id (str): Device ID + action (str): Action + + Returns: + str: Task ID + """ + if action not in ["one", "two"]: + raise ValueError(f"Unknown action {action}") + + response = self.post( + f"/v2/site/{site_id}/device/{device_id}/gate/perform/{action}", + json={}, + ) + LOGGER.info(f"Gate Action: {response.status_code} => {response.text}") + response.raise_for_status() + return response.json() + def get_devices_socket(self, site_id: str): """Get Devices Socket from a Site ID From a4a181a79f130aceb05889db1f5b08fa48a4b538 Mon Sep 17 00:00:00 2001 From: Minims Date: Sun, 22 Oct 2023 15:22:37 +0200 Subject: [PATCH 5/7] feature: add state fix: gate, light --- myFox2Mqtt/business/__init__.py | 34 +++++++++++++++++++++--- myFox2Mqtt/homeassistant/ha_discovery.py | 15 +++++------ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/myFox2Mqtt/business/__init__.py b/myFox2Mqtt/business/__init__.py index ad52e26..eb51f51 100644 --- a/myFox2Mqtt/business/__init__.py +++ b/myFox2Mqtt/business/__init__.py @@ -230,6 +230,26 @@ def ha_devices_config( retain=True, ) + + # State + for state_device in state_devices: + if state_device.get("deviceId") == device.device_id: + LOGGER.info( + f"Found State for {device.device_id}: {state_device.get('stateLabel')}" + ) + state = ha_discovery_devices( + site_id=site_id, + device=device, + mqtt_config=mqtt_config, + sensor_name="stateLabel", + ) + mqtt_publish( + mqtt_client=mqtt_client, + topic=state.get("topic"), + payload=state.get("config"), + retain=True, + ) + # Light for light_device in light_devices: if light_device.get("deviceId") == device.device_id: @@ -413,11 +433,9 @@ def update_devices_status( try: my_devices = api.get_devices(site_id=site_id) temperature_devices = api.get_devices_temperature(site_id=site_id) + other_devices = api.get_devices_other(site_id=site_id) light_devices = api.get_devices_light(site_id=site_id) state_devices = api.get_devices_state(site_id=site_id) - other_devices = api.get_devices_other(site_id=site_id) - camera_devices = api.get_devices_camera(site_id=site_id) - scenarios = api.get_scenarios(site_id=site_id) for device in my_devices: settings = device.settings @@ -441,6 +459,16 @@ def update_devices_status( if temperature_device.get("deviceId") == device.device_id: keys_values["lastTemperature"] = temperature_device.get("lastTemperature") + # Light + for light_device in light_devices: + if light_device.get("deviceId") == device.device_id: + keys_values["lastTemperature"] = light_device.get("light") + + # State + for state_device in state_devices: + if state_device.get("deviceId") == device.device_id: + keys_values["lastTemperature"] = state_device.get("stateLabel") + # Smoke for other_device in other_devices: if other_device.get("deviceId") == device.device_id: diff --git a/myFox2Mqtt/homeassistant/ha_discovery.py b/myFox2Mqtt/homeassistant/ha_discovery.py index 7c4213f..630a3ef 100644 --- a/myFox2Mqtt/homeassistant/ha_discovery.py +++ b/myFox2Mqtt/homeassistant/ha_discovery.py @@ -138,6 +138,13 @@ "pl_off": "False", }, }, + "stateLabel": { + "type": "binary_sensor", + "config": { + "pl_on": "opened", + "pl_off": "closed", + }, + }, "state": { "type": "binary_sensor", "config": { @@ -434,14 +441,6 @@ "type": "button", "config": {"payload_press": "halt"}, }, - "gate": { - "type": "switch", - "config": { - "pl_on": "gate_open", - "pl_off": "gate_close", - "optimistic": "True", - }, - }, "garage": { "type": "switch", "config": { From 3c63908c60cc948ea5f4b6922b3999da1168d77c Mon Sep 17 00:00:00 2001 From: Minims Date: Sun, 22 Oct 2023 15:30:51 +0200 Subject: [PATCH 6/7] fix: always push last history event --- myFox2Mqtt/business/__init__.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/myFox2Mqtt/business/__init__.py b/myFox2Mqtt/business/__init__.py index eb51f51..13ed762 100644 --- a/myFox2Mqtt/business/__init__.py +++ b/myFox2Mqtt/business/__init__.py @@ -230,13 +230,10 @@ def ha_devices_config( retain=True, ) - # State for state_device in state_devices: if state_device.get("deviceId") == device.device_id: - LOGGER.info( - f"Found State for {device.device_id}: {state_device.get('stateLabel')}" - ) + LOGGER.info(f"Found State for {device.device_id}: {state_device.get('stateLabel')}") state = ha_discovery_devices( site_id=site_id, device=device, @@ -387,20 +384,22 @@ def update_sites_status( payload = {} events = api.get_site_history(site_id=site_id) for event in events: + # Only Push last Event if event: - created_at = event.get("createdAt") - date_format = "%Y-%m-%dT%H:%M:%SZ" - created_at_date = datetime.strptime(created_at, date_format) - now = datetime.now() - if now - created_at_date < timedelta(seconds=70): - payload = f"{event.get('type')} {event.get('createdAt')} {event.get('label')}" - # Push status to MQTT - mqtt_publish( - mqtt_client=mqtt_client, - topic=f"{mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/history", - payload=payload, - retain=True, - ) + # created_at = event.get("createdAt") + # date_format = "%Y-%m-%dT%H:%M:%SZ" + # created_at_date = datetime.strptime(created_at, date_format) + # now = datetime.now() + # if now - created_at_date < timedelta(seconds=70): + payload = f"{event.get('type')} {event.get('createdAt')} {event.get('label')}" + # Push status to MQTT + mqtt_publish( + mqtt_client=mqtt_client, + topic=f"{mqtt_config.get('topic_prefix', 'myFox2mqtt')}/{site_id}/history", + payload=payload, + retain=True, + ) + break except Exception as exp: LOGGER.warning(f"Error while getting site history: {exp}") From 0af28dce3b898bbd259c49f2df48f37b3f17a3a9 Mon Sep 17 00:00:00 2001 From: Minims Date: Mon, 23 Oct 2023 20:55:33 +0200 Subject: [PATCH 7/7] fix: light & state update --- myFox2Mqtt/business/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/myFox2Mqtt/business/__init__.py b/myFox2Mqtt/business/__init__.py index 13ed762..e60eb90 100644 --- a/myFox2Mqtt/business/__init__.py +++ b/myFox2Mqtt/business/__init__.py @@ -461,12 +461,12 @@ def update_devices_status( # Light for light_device in light_devices: if light_device.get("deviceId") == device.device_id: - keys_values["lastTemperature"] = light_device.get("light") + keys_values["light"] = int(light_device.get("light")) # State for state_device in state_devices: if state_device.get("deviceId") == device.device_id: - keys_values["lastTemperature"] = state_device.get("stateLabel") + keys_values["stateLabel"] = state_device.get("stateLabel") # Smoke for other_device in other_devices: