diff --git a/homeassistant/components/guarddionisis/__init__.py b/homeassistant/components/guarddionisis/__init__.py new file mode 100644 index 00000000000000..0c74b3b841427e --- /dev/null +++ b/homeassistant/components/guarddionisis/__init__.py @@ -0,0 +1,24 @@ +"""Guard integration.""" +from threading import Thread +from homeassistant.components.guarddionisis.util.util import Util +from homeassistant import config_entries +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + + +async def async_setup(hass: HomeAssistant, config: dict): + theUtil = Util() + thread = Thread(target = theUtil.serve, args= (hass,)) + thread.start() + + return True + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + + return True \ No newline at end of file diff --git a/homeassistant/components/guarddionisis/alarm_control_panel.py b/homeassistant/components/guarddionisis/alarm_control_panel.py new file mode 100644 index 00000000000000..3adf16b5ce30af --- /dev/null +++ b/homeassistant/components/guarddionisis/alarm_control_panel.py @@ -0,0 +1,120 @@ +"""Interfaces with alarm control panels.""" +import logging + + +import voluptuous as vol + +import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) +from homeassistant.components.guarddionisis.util.util import DBAccess +from homeassistant.const import ( + CONF_ID, + CONF_NAME, + STATE_ALARM_ARMED_AWAY, + STATE_ALARM_ARMED_HOME, + STATE_ALARM_DISARMED, + STATE_ALARM_ARMED_DAY, + STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_TRIGGERED, +) +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "guarddionisis" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Optional(CONF_ID): cv.positive_int, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + } +) + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Set up a nightwatvhman control panel.""" + name = config.get(CONF_NAME) + id = config.get(CONF_ID) + guard = GuardAlarmEntity(hass, name, id) + async_add_entities([guard]) + + + +class GuardAlarmEntity(alarm.AlarmControlPanelEntity): + """Representation of an Alarm.com status.""" + + def __init__(self, hass, name,id): + """Initialize the Alarm.com status.""" + self.theDB = DBAccess('/home/dionisis/Database/TrackedObjectsDim.db') + _LOGGER.debug("Setting up dionisisalarm...") + self._hass = hass + self._name = name + self._id = id + #self._websession = async_get_clientsession(self._hass) + self._state = self.theDB.getAlarmState(id) + + #self._alarm = alarmDionisis(hass, name) + + # async def async_login(self): + # """Login to Alarm.com.""" + # await self._alarm.async_login() + + async def async_update(self): + """Fetch the latest state.""" + #await self._alarm.async_update() + return self.state + + @property + def name(self): + """Return the name of the alarm.""" + return self._name + + @property + def state(self): + """Return the state of the alarm.""" + return self._state + + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT | SUPPORT_ALARM_TRIGGER + + async def async_alarm_disarm(self, code=None): + """Send disarm command.""" + if await self.theDB.Disarm(self._id): + self._state = STATE_ALARM_DISARMED + await self.theDB.setAlarmState(self._id, self._state) + + async def async_alarm_arm_home(self, code=None): + """Send arm home command.""" + if await self.theDB.ArmHome(self._id): + self._state = STATE_ALARM_ARMED_HOME + await self.theDB.setAlarmState(self._id, self._state) + + async def async_alarm_arm_away(self, code=None): + """Send arm away command.""" + if await self.theDB.ArmAway(self._id): + self._state = STATE_ALARM_ARMED_AWAY + await self.theDB.setAlarmState(self._id, self._state) + + async def async_alarm_arm_night(self, code=None): + if await self.theDB.ArmNight(self._id): + self._state = STATE_ALARM_ARMED_NIGHT + await self.theDB.setAlarmState(self._id, self._state) + + async def async_alarm_trigger(self, code=None): + if await self.theDB.Disarm(self._id): #for now + self._state = STATE_ALARM_TRIGGERED + await self.theDB.setAlarmState(self._id, self._state) + + async def async_alarm_day(self, code=None): + if await self.theDB.ArmDay(self._id): #for now + self._state = STATE_ALARM_ARMED_DAY + await self.theDB.setAlarmState(self._id, self._state) \ No newline at end of file diff --git a/homeassistant/components/guarddionisis/const.py b/homeassistant/components/guarddionisis/const.py new file mode 100644 index 00000000000000..c2f2b9887ef67c --- /dev/null +++ b/homeassistant/components/guarddionisis/const.py @@ -0,0 +1,19 @@ +"""Constants for Video Guard integration.""" +import logging +from typing import Final + +DOMAIN = "guarddionisis" + +LOGGER = logging.getLogger(__package__) + +CONF_ID = "id" +CONF_TYPE = "type" + +SERVICE_SET_ALARM_STATUS = "set_alarm_status" +SERVICE_CLEAR_VIDEOS = "clear_videos" +SERVICE_SET_COUNTER = "set_counter" +SERVICE_INCREMENT_COUNTER = "increment_counter" +SERVICE_DEINCREMENT_COUNTER = "deincrement_counter" + +ATTR_ALARM_STATUS: Final = "alarm_status" +ATTR_COUNTER_VALUE: Final = "value" \ No newline at end of file diff --git a/homeassistant/components/guarddionisis/manifest.json b/homeassistant/components/guarddionisis/manifest.json new file mode 100644 index 00000000000000..d1583c262ae37b --- /dev/null +++ b/homeassistant/components/guarddionisis/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "guarddionisis", + "name": "Custom Video Guard", + "documentation": "https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor/", + "dependencies": [], + "codeowners": ["@dionisis"], + "requirements": ["grpcio==1.60.0","grpcio-tools==1.60.0"], + "iot_class": "local_push", + "version": "0.1.0", + "config_flow": false +} \ No newline at end of file diff --git a/homeassistant/components/guarddionisis/sensor.py b/homeassistant/components/guarddionisis/sensor.py new file mode 100644 index 00000000000000..a05a4b3ddfbb4b --- /dev/null +++ b/homeassistant/components/guarddionisis/sensor.py @@ -0,0 +1,229 @@ +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +import logging +from threading import Thread +from homeassistant.components.guarddionisis.util.util import DBAccess + +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_platform +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + + +from homeassistant.components.sensor import ( + SensorEntity, + +) + +from .const import ATTR_ALARM_STATUS, ATTR_COUNTER_VALUE, CONF_ID, CONF_TYPE, DOMAIN, LOGGER, SERVICE_CLEAR_VIDEOS, SERVICE_DEINCREMENT_COUNTER, SERVICE_INCREMENT_COUNTER, SERVICE_SET_ALARM_STATUS, SERVICE_SET_COUNTER +_LOGGER = logging.getLogger(__name__) + + +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA + +from homeassistant.const import ( + CONF_ID, + CONF_NAME +) +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "guarddionisis" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_ID): cv.positive_int, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_TYPE, default='area'): cv.string, + + } +) + +async def async_setup_platform( + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, +) -> None: + """Set up a guard control panel.""" + name = config.get(CONF_NAME) + id = config.get(CONF_ID) + type = config.get(CONF_TYPE) + GuardSensor = GuardSensorEntity(hass, name, id, type) + async_add_entities([GuardSensor]) + + # SERVICE_SET_ALARM_STATUS = "set_alarm_status" + # SERVICE_CLEAR_VIDEOS = "clear_videos" + # SERVICE_SET_COUNTER = "set_counter" + # SERVICE_INCREMENT_COUNTER = "increment_counter" + # SERVICE_DEINCREMENT_COUNTER = "deincrement_counter" + + platform = entity_platform.async_get_current_platform() + + platform.async_register_entity_service( + SERVICE_SET_ALARM_STATUS, + { + vol.Required(ATTR_ALARM_STATUS, default=0): vol.Coerce(int) + }, + "set_alarm_status", + # [SUPPORT_OPEN_DOOR], + ) + + platform.async_register_entity_service( + SERVICE_CLEAR_VIDEOS, + {}, + "clear_videos", + # [SUPPORT_OPEN_DOOR], + ) + platform.async_register_entity_service( + SERVICE_SET_COUNTER, + { + vol.Required(ATTR_COUNTER_VALUE, default=0): vol.Coerce(int) + }, + "set_counter", + # [SUPPORT_OPEN_DOOR], + ) + platform.async_register_entity_service( + SERVICE_INCREMENT_COUNTER, + {}, + "increment_counter", + # [SUPPORT_OPEN_DOOR], + ) + platform.async_register_entity_service( + SERVICE_DEINCREMENT_COUNTER, + {}, + "deincrement_counter", + # [SUPPORT_OPEN_DOOR], + ) + + +class GuardSensorEntity(SensorEntity): + """Represent an Guard sensor.""" + + def __init__( + self, + hass, + name, + id, + type + ): + self.theDB = DBAccess('/home/dionisis/Database/TrackedObjectsDim.db') + _LOGGER.debug("Setting up dionisissensor...") + self._hass = hass + self._name = name + self._id = id + self._type = type + self._state = self.theDB.getAreaCounter(id) if self._type =='area' else self.theDB.getRegionCounter(id) if self._type =='region' else self.theDB.getAlarmSensorStatus(id) + + @property + def name(self): + """Return the name of the alarm.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return "" if self._type == "alarm" else "people" + + @property + def should_poll(self): + return False + + # async def async_update(self): + # """Fetch new state data for the sensor.""" + # #return + # #self._state = self.theDB.getAreaCounter(self._id) if self._type =='area' else self.theDB.getRegionCounter(self._id) if self._type =='region' else self.theDB.getAlarmSensorStatus(self._id) + # self.async_update_ha_state() + + async def async_update(self): + # Manually update the state based on your custom asynchronous logic. + new_state = await self.async_calculate_new_state() + if new_state != self._state: + self._state = new_state + self.async_write_ha_state() + + async def async_calculate_new_state(self): + # Replace this with your custom asynchronous logic to determine the new state. + # For example, querying an external system, calculating, etc. + return self.theDB.getAreaCounter(self._id) if self._type =='area' else self.theDB.getRegionCounter(self._id) if self._type =='region' else self.theDB.getAlarmSensorStatus(self._id) + + + async def set_alarm_status(self,alarm_status): + try: + self.theDB.setAlarmSensorStatus(self._id,alarm_status) + self._state = alarm_status + # self.async_update() + await self.async_write_ha_state() + except: + lll=2 + + async def clear_videos(self): + try: + lll=1 + except: + lll=2 + + async def set_counter(self,value): + print("set_counter") + try: + lll=1 + if (self._type =='area'): + self.theDB.setAreaCounter(self._id,value) + elif (self._type == 'region'): + self.theDB.setRegionCounter(self._id,value) + elif (self._type == 'alarm'): + self.theDB.setAlarmSensorStatus(self._id,value) + self._state = value + await self.async_write_ha_state() + except: + lll=2 + + async def increment_counter(self): + try: + if (self._type =='area'): + tt = self.theDB.getAreaCounter(self._id) + tt = str(int(tt)+1) + self.theDB.setAreaCounter(self._id,tt) + self._state = tt + + else: + tt = self.theDB.getRegionCounter(self._id) + tt = str(int(tt)+1) + self.theDB.setRegionCounter(self._id,tt) + self._state = tt + await self.async_write_ha_state() + # self.async_update() + + except: + lll=2 + + async def deincrement_counter(self): + try: + if (self._type =='area'): + tt = self.theDB.getAreaCounter(self._id) + tt = str(int(tt)-1) + self.theDB.setAreaCounter(self._id,tt) + self._state = tt + else: + tt = self.theDB.getRegionCounter(self._id) + tt = str(int(tt)-1) + self.theDB.setRegionCounter(self._id,tt) + self._state = tt + await self.async_write_ha_state() + + except: + lll=2 diff --git a/homeassistant/components/guarddionisis/services.yaml b/homeassistant/components/guarddionisis/services.yaml new file mode 100644 index 00000000000000..89c1843b18921a --- /dev/null +++ b/homeassistant/components/guarddionisis/services.yaml @@ -0,0 +1,91 @@ +set_alarm_status: + description: send command to set the status of the alarm + fields: + entity_id: + name: Entity Name + description: Name of the target entity + required: true + example: "sensor.home_alarm" + selector: + entity: + integration: guarddionisis + status: + name: New status + description: new status value + required: true + example: "Alert" + selector: + text: + +clear_videos: + description: send command to clear all videos + +set_counter: + description: set the counter of an area + fields: + entity_id: + name: Entity Name + description: Name of the target counter + required: true + example: "sensor.front_garden" + selector: + entity: + integration: guarddionisis + value: + name: New value + description: new status value + required: true + example: "2" + selector: + number: + min: 0 + max: 250 + step: 1 + mode: box + +set_alarm_status: + description: set the status of the alarm + fields: + entity_id: + name: Entity Name + description: Name of the target counter + required: true + example: "sensor.homealarm" + selector: + entity: + integration: guarddionisis + alarm_status: + name: New status + description: new status value + required: true + example: "2" + selector: + number: + min: 0 + max: 4 + step: 1 + mode: box + +increment_counter: + description: increment the counter of an area + fields: + entity_id: + name: Entity Name + description: Name of the target counter + required: true + example: "sensor.front_garden" + selector: + entity: + integration: guarddionisis + +deincrement_counter: + description: deincrement the counter of an area + fields: + entity_id: + name: Entity Name + description: Name of the target counter + required: true + example: "sensor.front_garden" + selector: + entity: + integration: guaguarddionisisrd \ No newline at end of file diff --git a/homeassistant/components/guarddionisis/strings.json b/homeassistant/components/guarddionisis/strings.json new file mode 100644 index 00000000000000..59630e87932d52 --- /dev/null +++ b/homeassistant/components/guarddionisis/strings.json @@ -0,0 +1,101 @@ +{ + "config": { + "step": { + "user": { + "description": "Configure a local Elexa Guardian device.", + "data": { + "ip_address": "[%key:common::config_flow::data::ip%]", + "port": "[%key:common::config_flow::data::port%]" + } + }, + "discovery_confirm": { + "description": "Do you want to set up this Guardian device?" + } + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + } + }, + "entity": { + "binary_sensor": { + "leak": { + "name": "Leak detected" + }, + "moved": { + "name": "Recently moved" + } + }, + "button": { + "reset_diagnostics": { + "name": "Reset valve diagnostics" + } + }, + "sensor": { + "uptime": { + "name": "Uptime" + } + }, + "switch": { + "onboard_access_point": { + "name": "Onboard access point" + }, + "valve_controller": { + "name": "Valve controller" + } + } + }, + "services": { + "pair_sensor": { + "name": "Pair sensor", + "description": "Adds a new paired sensor to the valve controller.", + "fields": { + "device_id": { + "name": "[%key:component::guardian::entity::switch::valve_controller::name%]", + "description": "The valve controller to add the sensor to." + }, + "uid": { + "name": "UID", + "description": "The UID of the paired sensor." + } + } + }, + "unpair_sensor": { + "name": "Unpair sensor", + "description": "Removes a paired sensor from the valve controller.", + "fields": { + "device_id": { + "name": "[%key:component::guardian::entity::switch::valve_controller::name%]", + "description": "The valve controller to remove the sensor from." + }, + "uid": { + "name": "[%key:component::guardian::services::pair_sensor::fields::uid::name%]", + "description": "[%key:component::guardian::services::pair_sensor::fields::uid::description%]" + } + } + }, + "upgrade_firmware": { + "name": "Upgrade firmware", + "description": "Upgrades the device firmware.", + "fields": { + "device_id": { + "name": "[%key:component::guardian::entity::switch::valve_controller::name%]", + "description": "The valve controller whose firmware should be upgraded." + }, + "url": { + "name": "[%key:common::config_flow::data::url%]", + "description": "The URL of the server hosting the firmware file." + }, + "port": { + "name": "[%key:common::config_flow::data::port%]", + "description": "The port on which the firmware file is served." + }, + "filename": { + "name": "Filename", + "description": "The firmware filename." + } + } + } + } +} diff --git a/homeassistant/components/guarddionisis/util/sensor.proto b/homeassistant/components/guarddionisis/util/sensor.proto new file mode 100644 index 00000000000000..9a90a8ce2b42d6 --- /dev/null +++ b/homeassistant/components/guarddionisis/util/sensor.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package sensor; + + +message SensorData { + string entity_id = 1; + int32 value = 2; +} + +message Void { +} + +service SensorService { + rpc GetSensorData (Void) returns (SensorData); + rpc SetSensorData (SensorData) returns (Void); +} diff --git a/homeassistant/components/guarddionisis/util/sensor_pb2.py b/homeassistant/components/guarddionisis/util/sensor_pb2.py new file mode 100644 index 00000000000000..f7e420b14fd0e8 --- /dev/null +++ b/homeassistant/components/guarddionisis/util/sensor_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: homeassistant/components/guarddionisis/util/sensor.proto +# Protobuf Python Version: 4.25.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n8homeassistant/components/guarddionisis/util/sensor.proto\x12\x06sensor\".\n\nSensorData\x12\x11\n\tentity_id\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05\"\x06\n\x04Void2u\n\rSensorService\x12\x31\n\rGetSensorData\x12\x0c.sensor.Void\x1a\x12.sensor.SensorData\x12\x31\n\rSetSensorData\x12\x12.sensor.SensorData\x1a\x0c.sensor.Voidb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'homeassistant.components.guarddionisis.util.sensor_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_SENSORDATA']._serialized_start=68 + _globals['_SENSORDATA']._serialized_end=114 + _globals['_VOID']._serialized_start=116 + _globals['_VOID']._serialized_end=122 + _globals['_SENSORSERVICE']._serialized_start=124 + _globals['_SENSORSERVICE']._serialized_end=241 +# @@protoc_insertion_point(module_scope) diff --git a/homeassistant/components/guarddionisis/util/sensor_pb2_grpc.py b/homeassistant/components/guarddionisis/util/sensor_pb2_grpc.py new file mode 100644 index 00000000000000..021dd02043428f --- /dev/null +++ b/homeassistant/components/guarddionisis/util/sensor_pb2_grpc.py @@ -0,0 +1,99 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from homeassistant.components.guarddionisis.util import sensor_pb2 as homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2 + + +class SensorServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetSensorData = channel.unary_unary( + '/sensor.SensorService/GetSensorData', + request_serializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.Void.SerializeToString, + response_deserializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.SensorData.FromString, + ) + self.SetSensorData = channel.unary_unary( + '/sensor.SensorService/SetSensorData', + request_serializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.SensorData.SerializeToString, + response_deserializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.Void.FromString, + ) + + +class SensorServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def GetSensorData(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SetSensorData(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_SensorServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GetSensorData': grpc.unary_unary_rpc_method_handler( + servicer.GetSensorData, + request_deserializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.Void.FromString, + response_serializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.SensorData.SerializeToString, + ), + 'SetSensorData': grpc.unary_unary_rpc_method_handler( + servicer.SetSensorData, + request_deserializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.SensorData.FromString, + response_serializer=homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.Void.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'sensor.SensorService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class SensorService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def GetSensorData(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/sensor.SensorService/GetSensorData', + homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.Void.SerializeToString, + homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.SensorData.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SetSensorData(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/sensor.SensorService/SetSensorData', + homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.SensorData.SerializeToString, + homeassistant_dot_components_dot_guarddionisis_dot_util_dot_sensor__pb2.Void.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/homeassistant/components/guarddionisis/util/testclient.py b/homeassistant/components/guarddionisis/util/testclient.py new file mode 100644 index 00000000000000..2b6f52f414ec03 --- /dev/null +++ b/homeassistant/components/guarddionisis/util/testclient.py @@ -0,0 +1,23 @@ +import grpc +import sensor_pb2 +import sensor_pb2_grpc + +def run(): + with grpc.insecure_channel('localhost:52751') as channel: + stub = sensor_pb2_grpc.SensorServiceStub(channel) + + # Get sensor data + # response = stub.GetSensorData(sensor_pb2.Empty()) + # print(f"Received sensor data: ID={response.id}, Value={response.value}") + + # Set new sensor data + new_data = sensor_pb2.SensorData(entity_id="sensor.homealarm", value=3) + stub.SetSensorData(new_data) + print("Set new sensor data.") + + # Get updated sensor data + # response = stub.GetSensorData(sensor_pb2.Empty()) + # print(f"Received updated sensor data: ID={response.id}, Value={response.value}") + +if __name__ == '__main__': + run() diff --git a/homeassistant/components/guarddionisis/util/util.py b/homeassistant/components/guarddionisis/util/util.py new file mode 100644 index 00000000000000..e3f7ec9bfd0538 --- /dev/null +++ b/homeassistant/components/guarddionisis/util/util.py @@ -0,0 +1,256 @@ +"""Define Guard specific utilities.""" +from concurrent import futures +import importlib +import sqlite3 +from sqlite3 import Error +from typing import Any, Mapping +import logging + + +#sensor_pb2 = importlib.util.spec_from_file_location("sensor_pb2", "/Users/dionisis/Code/GitHub/core/homeassistant/components/guarddionisis/util/sensor_pb2.py") +#sensor_pb2_grpc = importlib.util.spec_from_file_location("sensor_pb2_grpc", "/Users/dionisis/Code/GitHub/core/homeassistant/components/guarddionisis/util/sensor_pb2_grpc.py") + +import grpc +import homeassistant.components.guarddionisis.util.sensor_pb2 as sensor_pb2 +import homeassistant.components.guarddionisis.util.sensor_pb2_grpc as sensor_pb2_grpc + +from homeassistant.core import HomeAssistant, State + + +_LOGGER = logging.getLogger(__name__) + + +class Util: + def serve(self,hass): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + sensor_pb2_grpc.add_SensorServiceServicer_to_server(SensorServicer(hass), server) + server.add_insecure_port('[::]:52751') + server.start() + print("gRPC server started. Listening on port 52751.") + server.wait_for_termination() + +class DBAccess: + def __init__(self, db_file): + self.conn = None + try: + self.conn = sqlite3.connect(db_file) + except Error as e: + a=1 + + def getAreaCounter(self, ID): + try: + cur = self.conn.cursor() + cur.execute("SELECT counted FROM AreaMonitoring WHERE AreaID=?", (ID,)) + rows = cur.fetchall() + state = rows[0][0] + cur.close() + return state + except: + return -1 + + def setAreaCounter(self, ID, status): + try: + cur = self.conn.cursor() + sql = 'Update AreaMonitoring set counted = ? where AreaID = ?' + data = (status, ID) + cur.execute(sql, data) + self.conn.commit() + cur.close() + except: + return False + return True + + def getRegionCounter(self, ID): + try: + cur = self.conn.cursor() + cur.execute("SELECT count FROM RegionMonitoring WHERE RegionID=?", (ID,)) + rows = cur.fetchall() + state = rows[0][0] + cur.close() + return state + except: + return -1 + + def setRegionCounter(self, ID, status): + try: + cur = self.conn.cursor() + sql = 'Update RegionMonitoring set count = ? where RegionID = ?' + data = (status, ID) + cur.execute(sql, data) + self.conn.commit() + cur.close() + except: + return False + return True + + + + def getAlarmSensorStatus(self, ID): + try: + cur = self.conn.cursor() + cur.execute("SELECT AlarmStatus FROM AreaMonitoring WHERE AreaID=?", (ID,)) + rows = cur.fetchall() + state = rows[0][0] + cur.close() + return state + except: + return -1 + + def setAlarmSensorStatus(self, ID, status): + try: + cur = self.conn.cursor() + sql = 'Update AreaMonitoring set AlarmStatus = ? where AreaID = ?' + data = (status, ID) + cur.execute(sql, data) + self.conn.commit() + cur.close() + except: + return False + return True + + def getAlarmState(self, ID): + try: + cur = self.conn.cursor() + cur.execute("SELECT HAAlarmState FROM Areas WHERE ID=?", (ID,)) + rows = cur.fetchall() + cur.close() + state = rows[0][0] + return state + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:" + str(e)) + return "error" + + async def setAlarmState(self, ID, state): + try: + cur = self.conn.cursor() + sql = 'Update Areas set HAAlarmState = ? where id = ?' + data = (state, ID) + cur.execute(sql, data) + self.conn.commit() + cur.close() + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:" + str(e)) + return False + return True + + async def Disarm(self, ID): + try: + cur = self.conn.cursor() + #sql = 'Update ROIs set active = 1 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("gate","entrance","passage")' + sql = 'Update ROIs set active = 1 where type in ("gate","entrance","passage")' + data = (ID,) + cur.execute(sql) + self.conn.commit() + cur.close() + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:" + str(e)) + return False + return True + + async def ArmDay(self, ID): + try: + cur = self.conn.cursor() + sql = 'Update ROIs set active = 1 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("gate","entrance","passage")' + data = (ID,) + cur.execute(sql) + self.conn.commit() + cur.close() + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:" + str(e)) + return False + return True + + async def ArmNight(self, ID): + try: + cur = self.conn.cursor() + #sql = 'Update ROIs set active = 0 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("gate","entrance")' + sql = 'Update ROIs set active = 0 where type in ("gate","entrance")' + data = (ID,) + cur.execute(sql) + + #sql = 'Update ROIs set active = 1 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("passage")' + sql = 'Update ROIs set active = 1 where type in ("passage")' + data = (ID,) + cur.execute(sql) + + #sql = 'Update AreaMonitoring set counted = 0 where areaid in (select id from areas where masterarea_ID = ? and AreaType = \'perimeter\')' + sql = 'Update AreaMonitoring set counted = 0 where areaid in (select id from areas where AreaType = \'perimeter\')' + data = (ID,) + cur.execute(sql) + + self.conn.commit() + cur.close() + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:" + str(e)) + return False + return True + + async def ArmHome(self, ID): + try: + cur = self.conn.cursor() + #dionisis thelei douleia edw + #sql = 'Update ROIs set active = 0 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("entrance")' + sql = 'Update ROIs set active = 0 where type in ("entrance")' + data = (ID,) + cur.execute(sql) + + #sql = 'Update ROIs set active = 1 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("gate","passage")' + sql = 'Update ROIs set active = 1 where type in ("gate","passage")' + data = (ID,) + cur.execute(sql) + self.conn.commit() + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:" + str(e)) + return False + return True + + async def ArmAway(self, ID): + try: + cur = self.conn.cursor() + #sql = 'Update ROIs set active = 0 where id in (select roi_id from roisinareas inner join areas on roisinareas.area_id = areas.id where areas.MasterArea_ID = ?) and type in ("gate","entrance","passage")' + sql = 'Update ROIs set active = 0 where type in ("gate","entrance","passage")' + data = (ID,) + cur.execute(sql) + + #sql = 'Update AreaMonitoring set counted = 0 where areaid in (select id from areas where masterarea_ID = ?)' + #data = (ID,) + #cur.execute(sql, data) + + self.conn.commit() + cur.close() + except Error as e: + _LOGGER.error("AlarmDionisis DB exception:"+str(e)) + return False + return True + + + +class SensorServicer(sensor_pb2_grpc.SensorServiceServicer): + def __init__(self,hass:HomeAssistant): + self.sensor_data = None + self.hass = hass + + def GetSensorData(self, request, context): + return 0 + + def SetSensorData(self, request, context): + self.test = request.value + #llll = self.hass.states + #self.hass.states._states["sensor.internal"]= request.value + print("SetSensorData entity:"+str(request.entity_id)+" value:"+str(request.value)) + + entity = self.hass.states.get(request.entity_id) + attr = entity.attributes#{'unit_of_measurement':'People'} + + + #self.hass.services.call('sensor', 'set_state', {'entity_id': request.entity_id, 'value': request.value}) + + #new_state = State(request.entity_id, request.value, dict(entity.attributes)) + + self.hass.states.set(request.entity_id, request.value,attr) + + return sensor_pb2.Void() + + + + diff --git a/homeassistant/components/input_number/__init__.py b/homeassistant/components/input_number/__init__.py index 48a9952ab1833e..4a74201be15faf 100644 --- a/homeassistant/components/input_number/__init__.py +++ b/homeassistant/components/input_number/__init__.py @@ -25,15 +25,12 @@ import homeassistant.helpers.service from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType -import sqlite3 -from sqlite3 import Error _LOGGER = logging.getLogger(__name__) DOMAIN = "input_number" CONF_INITIAL = "initial" -CONF_AREAID = "areaid" CONF_MIN = "min" CONF_MAX = "max" CONF_STEP = "step" @@ -70,7 +67,6 @@ def _cv_input_number(cfg): vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)), vol.Required(CONF_MIN): vol.Coerce(float), vol.Required(CONF_MAX): vol.Coerce(float), - vol.Required(CONF_AREAID): vol.Coerce(int), vol.Optional(CONF_INITIAL): vol.Coerce(float), vol.Optional(CONF_STEP, default=1): vol.All(vol.Coerce(float), vol.Range(min=1e-9)), vol.Optional(CONF_ICON): cv.icon, @@ -86,7 +82,6 @@ def _cv_input_number(cfg): vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_MIN): vol.Coerce(float), vol.Required(CONF_MAX): vol.Coerce(float), - vol.Required(CONF_AREAID): vol.Coerce(int), vol.Optional(CONF_INITIAL): vol.Coerce(float), vol.Optional(CONF_STEP, default=1): vol.All( vol.Coerce(float), vol.Range(min=1e-9) @@ -168,37 +163,6 @@ async def reload_service_handler(service_call: ServiceCall) -> None: return True -class DBAccess: - def __init__(self, db_file): - self.conn = None - try: - self.conn = sqlite3.connect(db_file) - except Error as e: - a=1 - - def getValue(self, ID): - try: - cur = self.conn.cursor() - cur.execute("SELECT counted FROM AreaMonitoring WHERE areaid=?", (ID,)) - rows = cur.fetchall() - value = rows[0][0] - cur.close() - return value - except: - return "error" - - async def setValue(self, ID, count): - try: - cur = self.conn.cursor() - sql = 'Update AreaMonitoring set counted = ? where areaid = ?' - data = (count, ID) - cur.execute(sql, data) - self.conn.commit() - cur.close() - except: - return False - return True - class NumberStorageCollection(collection.DictStorageCollection): """Input storage based collection.""" @@ -242,15 +206,13 @@ class InputNumber(collection.CollectionEntity, RestoreEntity): {ATTR_EDITABLE, ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_STEP} ) - _attr_should_poll = True + _attr_should_poll = False editable: bool def __init__(self, config: ConfigType) -> None: """Initialize an input number.""" self._config = config self._current_value: float | None = config.get(CONF_INITIAL) - self.theDB = DBAccess('/home/dionisis/Database/TrackedObjectsDim.db') - @classmethod def from_storage(cls, config: ConfigType) -> Self: @@ -290,7 +252,6 @@ def icon(self): @property def state(self): """Return the state of the component.""" - self._current_value = self.theDB.getValue( self._config.get(CONF_AREAID)) return self._current_value @property @@ -340,13 +301,13 @@ async def async_added_to_hass(self): async def async_set_value(self, value): """Set new value.""" num_value = float(value) - + if num_value < self._minimum or num_value > self._maximum: raise vol.Invalid( f"Invalid value for {self.entity_id}: {value} (range {self._minimum} -" f" {self._maximum})" ) - await self.theDB.setValue(self._config.get(CONF_AREAID),num_value) + self._current_value = num_value self.async_write_ha_state() @@ -366,4 +327,4 @@ async def async_update_config(self, config: ConfigType) -> None: return self._current_value = min(self._current_value, self._maximum) self._current_value = max(self._current_value, self._minimum) - self.async_write_ha_state() \ No newline at end of file + self.async_write_ha_state()