""" A device for pypglab.""" from __future__ import annotations from typing import cast from voluptuous import Schema, ALLOW_EXTRA, REMOVE_EXTRA, MultipleInvalid from . import validation as cv from .const import ( E_BOARD, E_SWITCH, LOGGER, PGLAB_DEVICE_TYPES, STATUS_SENSOR_CONFIG, CONFIG_MAC, CONFIG_IP, CONFIG_ID, CONFIG_NAME, CONFIG_PARAMETERS, CONFIG_TYPE, CONFIG_MANUFACTURER, CONFIG_HARDWARE_VERSION, CONFIG_FIRMWARE_VERSION, CONFIG_EBOARD_SHUTTERS, CONFIG_EBOARD_BOARDS, ) from .mqtt import Client from .relay import CreateRelay from .sensor import CreateStatusSensor from .shutter import CreateShutter PGLAB_DISCOVERY_SCHEMA = Schema( { CONFIG_IP: cv.ipaddrs, CONFIG_MAC: cv.macaddrs, CONFIG_NAME: cv.string, CONFIG_HARDWARE_VERSION: cv.version, CONFIG_FIRMWARE_VERSION: cv.version, CONFIG_TYPE: cv.devicetype, CONFIG_ID: cv.string, CONFIG_MANUFACTURER: cv.manufacturer, }, required=True, extra=ALLOW_EXTRA, ) PGLAB_EBOARD_PARAMETERS = Schema ( { CONFIG_PARAMETERS: { CONFIG_EBOARD_SHUTTERS: cv.shutter, CONFIG_EBOARD_BOARDS: cv.boards, } }, required=True, extra=REMOVE_EXTRA, ) class Device: """The class represent a generic PG LAB device.""" def __init__(self) -> None: """Initialize.""" # device ip address self._ip = None # the device mac address self._mac = None # device unique id along all PG Lab devices self._id = None # device friendly name for mqtt connection self._name = None # device name type self._type = None # the manufacturer of the device self._manufacturer = None # the hardware version self._hardware_version = None # the firmware version self._firmware_version = None # specific parameters of the device self._parameters = None # prepare an array of available relays self._relays = [] # prepare an array of available shutters self._shutters = [] # prepare the status sensor of the device self._status_sensor = None # internal configuration hash self._hash = hash( ( self._id, self._name, ) ) async def config(self, mqtt: Client, config: dict, subscribe:bool = False) -> bool: """Perform internal configuration.""" # validate config message try: config = PGLAB_DISCOVERY_SCHEMA(config) except MultipleInvalid as e: LOGGER.warning("Invalid discovey message (%s)", e) return False self._mac = config[CONFIG_MAC] self._ip = config[CONFIG_IP] self._id = config[CONFIG_ID] self._name = config[CONFIG_NAME] self._type = config[CONFIG_TYPE] self._manufacturer = config[CONFIG_MANUFACTURER] self._hardware_version = config[CONFIG_HARDWARE_VERSION] self._firmware_version = config[CONFIG_FIRMWARE_VERSION] # update the device hash self._hash = hash((self._id, self._name, self._mac)) # initialize the specific type of device if self.is_eboard: # validate e-board parameters try: eboard_config = PGLAB_EBOARD_PARAMETERS(config) except MultipleInvalid as e: LOGGER.warning("Invalid E-Board parameters message (%s)", e) return False # save the e-board parameters self._parameters = eboard_config[CONFIG_PARAMETERS] # update the device hash with specific e-board configuration self._hash = hash( ( self._hash, self._parameters.get(CONFIG_EBOARD_SHUTTERS), self._parameters.get(CONFIG_EBOARD_BOARDS), ) ) shutters = self._parameters.get(CONFIG_EBOARD_SHUTTERS) # prepare all shutters for index in range(0, shutters): if await self.is_relay_connected(index * 2): shutter = await CreateShutter(self._id, self._name, index, mqtt) if subscribe: await shutter.subscribe_topics() self._shutters.append(shutter) # prepare all relays for index in range(2 * shutters, 64): if await self.is_relay_connected(index): relay = await CreateRelay(self._id, self._name, index, mqtt) if subscribe: await relay.subscribe_topics() self._relays.append(relay) # prepare the sensor self._status_sensor = await CreateStatusSensor( self._id, self._name, STATUS_SENSOR_CONFIG[self._type], mqtt ) if subscribe: await self._status_sensor.subscribe_topics() return True async def is_relay_connected(self, index): """Return if a specific relay is connected to e-board and available to use.""" if self.is_eboard: if index < 0 or index > 63: return False boards = self._parameters[CONFIG_EBOARD_BOARDS] bi = index // 8 return boards[bi] == "1" else: return False @property def is_eboard(self): """Return true if it is a E-Board device.""" return self.type == PGLAB_DEVICE_TYPES[E_BOARD] @property def is_eswitch(self): """Return true if it is a E-Switch device.""" return self.type == PGLAB_DEVICE_TYPES[E_SWITCH] @property def relays(self): """Get the relay array.""" return self._relays @property def shutters(self): """Get the shutter array.""" return self._shutters @property def status_sensor(self): """Get the device status sensor.""" return self._status_sensor @property def ip(self) -> str: """Device ip.""" return cast(str, self._ip) @property def mac(self) -> str: """Device mac.""" return cast(str, self._mac) @property def id(self) -> str: """Device unique id.""" return cast(str, self._id) @property def name(self) -> str: """Device friendly name.""" return cast(str, self._name) @property def type(self) -> str: """Device type name (E-Board, E-Switch etc...).""" return cast(str, self._type) @property def manufacturer(self) -> str: """Device manufacturer.""" return cast(str, self._manufacturer) @property def hardware_version(self) -> str: """Device hardware version.""" return cast(str, self._hardware_version) @property def firmware_version(self) -> str: """Device firmware version.""" return cast(str, self._firmware_version) @property def hash(self) -> int: """Return device HASH.""" return cast(int, self._hash)