def set_hostname(self, hostname): """ Set raspi hostname Args: hostname (string): hostname Returns: bool: True if hostname saved successfully, False otherwise Raises: InvalidParameter: if hostname has invalid format """ # check hostname if re.match(self.__hostname_pattern, hostname) is None: raise InvalidParameter("Hostname is not valid") # update hostname res = self.hostname.set_hostname(hostname) # send event to update hostname on all devices if res: self.hostname_update_event.send(params={"hostname": hostname}) return res
def install_driver(self, driver_type, driver_name, force=False): """ Install specified driver. If install succeed, device will reboot after a delay Args: driver_type (string): driver type driver_name (string): driver name force (bool): force install (repair) Raises: MissingParameter: if a parameter is missing InvalidParameter: if driver was not found CommandInfo: if driver already installed """ self._check_parameters([ { 'name': 'driver_type', 'type': str, 'value': driver_type }, { 'name': 'driver_name', 'type': str, 'value': driver_name }, { 'name': 'force', 'type': bool, 'value': force }, ]) # get driver driver = self.drivers.get_driver(driver_type, driver_name) if not driver: raise InvalidParameter('No driver found for specified parameters') if not force and driver.is_installed(): raise CommandInfo('Driver is already installed') # launch installation (non blocking) and send event driver.install(self._install_driver_terminated, logger=self.logger) self.driver_install_event.send({ 'drivertype': driver_type, 'drivername': driver_name, 'installing': True, 'success': None, 'message': None, })
def update_gpio(self, device_uuid, name, keep, inverted, command_sender): """ Update gpio Args: device_uuid (string): device identifier name (string): gpio name keep (bool): keep status flag inverted (bool): inverted flag command_sender (string): command sender Returns: dict: updated gpio device Raises: CommandError MissingParameter Unauthorized InvalidParameter """ # fix command_sender: rpcserver is the default gpio entry point if command_sender == "rpcserver": command_sender = "gpios" # check values self._check_parameters( [ {"name": "device_uuid", "value": device_uuid, "type": str}, {"name": "name", "value": name, "type": str}, {"name": "keep", "value": keep, "type": bool}, {"name": "inverted", "value": inverted, "type": bool}, ] ) device = self._get_device(device_uuid) if device is None: raise InvalidParameter('Device "%s" does not exist' % device_uuid) if device["owner"] != command_sender: raise Unauthorized("Device can only be updated by its owner") # device is valid, update entry device["name"] = name device["keep"] = keep device["inverted"] = inverted if not self._update_device(device_uuid, device): raise CommandError('Failed to update device "%s"' % device["uuid"]) # relaunch watcher self._reconfigure_gpio(device) return device
def set_position(self, latitude, longitude): """ Set device position Args: latitude (float): latitude longitude (float): longitude Raises: CommandError: if error occured during position saving """ if latitude is None: raise MissingParameter('Parameter "latitude" is missing') if not isinstance(latitude, float): raise InvalidParameter('Parameter "latitude" is invalid') if longitude is None: raise MissingParameter('Parameter "longitude" is missing') if not isinstance(longitude, float): raise InvalidParameter('Parameter "longitude" is invalid') # save new position position = {"latitude": latitude, "longitude": longitude} if not self._set_config_field("position", position): raise CommandError("Unable to save position") # reset python time to take into account last modifications before # computing new times time.tzset() # and update related stuff self.set_timezone() self.set_country() self.set_sun() # send now event self._time_task()
def uninstall_driver(self, driver_type, driver_name): """ Uninstall specified driver. If uninstall succeed, device will reboot after a delay. Args: driver_type (string): driver type driver_name (string): driver name Raises: MissingParameter: if a parameter is missing InvalidParameter: if driver was not found CommandInfo: if driver is not installed """ self._check_parameters([ { 'name': 'driver_type', 'type': str, 'value': driver_type }, { 'name': 'driver_name', 'type': str, 'value': driver_name }, ]) # get driver instance driver = self.drivers.get_driver(driver_type, driver_name) if not driver: raise InvalidParameter('No driver found for specified parameters') if not driver.is_installed(): raise CommandInfo('Driver is not installed') # launch uninstallation (non blocking) and send event driver.uninstall(self._uninstall_driver_terminated, logger=self.logger) self.driver_uninstall_event.send({ 'drivertype': driver_type, 'drivername': driver_name, 'uninstalling': True, 'success': None, 'message': None, }) return True
def check_application(self, module_name): """ Check application content Args: module_name (string): module name Returns: dict: archive infos:: { url (string): archive url, name (string): archive name (usually name of module) } """ # check parameters if module_name is None or len(module_name) == 0: raise MissingParameter('Parameter "module_name" is missing') module_path = os.path.join(self.cleep_path, 'modules', module_name, '%s.py' % module_name) if not os.path.exists(module_path): raise InvalidParameter('Module "%s" does not exist' % module_name) # execute checks backend_result = self.__cli_check(self.CLI_CHECK_BACKEND_CMD % (self.CLI, module_name)) frontend_result = self.__cli_check(self.CLI_CHECK_FRONTEND_CMD % (self.CLI, module_name)) scripts_result = self.__cli_check(self.CLI_CHECK_SCRIPTS_CMD % (self.CLI, module_name)) tests_result = self.__cli_check(self.CLI_CHECK_TESTS_CMD % (self.CLI, module_name)) # code_result = self.__cli_check(self.CLI_CHECK_CODE_CMD % (self.CLI, module_name)) changelog_result = self.__cli_check(self.CLI_CHECK_CHANGELOG_CMD % (self.CLI, module_name)) return { 'backend': backend_result, 'frontend': frontend_result, 'scripts': scripts_result, 'tests': tests_result, 'changelog': changelog_result, # 'quality': code_result, }
def delete_gpio(self, device_uuid, command_sender): """ Delete gpio Args: uuid: device identifier command_sender (string): command sender Returns: bool: True if device was deleted, False otherwise Raises: CommandError MissingParameter Unauthorized InvalidParameter """ # fix command_sender: rpcserver is the default gpio entry point if command_sender == "rpcserver": command_sender = "gpios" # check values self._check_parameters( [ {"name": "device_uuid", "value": device_uuid, "type": str}, ] ) device = self._get_device(device_uuid) if device is None: raise InvalidParameter('Device "%s" does not exist' % device_uuid) if device["owner"] != command_sender: raise Unauthorized("Device can only be deleted by its owner") # device is valid, remove entry if not self._delete_device(device_uuid): raise CommandError('Failed to delete device "%s"' % device["uuid"]) self._deconfigure_gpio(device) return True
def add(self, name, gpio, interval, offset, offset_unit): """ Return sensor data to add. Can perform specific stuff Args: name (string): sensor name gpio (string): gpio name interval (int): interval value offset (int): offset value offset_unit (string): offset unit Returns: dict: sensor data to add:: { gpios (list): list of gpios data to add sensors (list): list sensors data to add } """ # get assigned gpios assigned_gpios = self._get_assigned_gpios() # check parameters self._check_parameters([ { "name": "name", "value": name, "type": str, "validator": lambda val: self._search_device("name", val) is None, "message": 'Name "%s" is already used' % name, }, { "name": "gpio", "value": gpio, "type": str, "validator": lambda val: gpio not in assigned_gpios, "message": 'Gpio "%s" is already used' % gpio, }, { "name": "interval", "value": interval, "type": int, "validator": lambda val: val >= 60, "message": "Interval must be greater or equal than 60", }, { "name": "offset", "value": offset, "type": int, }, { "name": "offset_unit", "value": offset_unit, "type": str, "validator": lambda val: val in (SensorsUtils.TEMP_CELSIUS, SensorsUtils.TEMP_FAHRENHEIT), "message": 'Offset_unit value must be either "celsius" or "fahrenheit"', }, ]) # TODO add new validator in cleep v0.0.27 if gpio not in self.raspi_gpios: raise InvalidParameter( 'Gpio "%s" does not exist for this raspberry pi' % gpio) gpio_data = { "name": name + "_dht22", "gpio": gpio, "mode": "input", "keep": False, "inverted": False, } temperature_data = { "name": name, "gpios": [], "type": self.TYPE_TEMPERATURE, "subtype": self.SUBTYPE, "interval": interval, "offset": offset, "offsetunit": offset_unit, "lastupdate": int(time.time()), "celsius": None, "fahrenheit": None, } humidity_data = { "name": name, "gpios": [], "type": self.TYPE_HUMIDITY, "subtype": self.SUBTYPE, "interval": interval, "lastupdate": int(time.time()), "humidity": None, } return { "gpios": [ gpio_data, ], "sensors": [ temperature_data, humidity_data, ], }
def select_device(self, driver_name): """ Select audio device Args: driver_name (string): driver name Returns: bool: True if device saved successfully Raises: InvalidParameter: if parameter is invalid """ # check params self._check_parameters( [ {"name": "driver_name", "type": str, "value": driver_name}, { "name": "driver_name", "type": str, "value": driver_name, "validator": lambda val: driver_name != self._get_config_field("driver"), "message": f'Device "{driver_name}" is already selected', }, ] ) # get drivers selected_driver_name = self._get_config_field("driver") old_driver = ( self.drivers.get_driver(Driver.DRIVER_AUDIO, selected_driver_name) if selected_driver_name is not None else None ) new_driver = self.drivers.get_driver(Driver.DRIVER_AUDIO, driver_name) if not new_driver: raise InvalidParameter("Specified driver does not exist") if not new_driver.is_installed(): raise InvalidParameter( "Can't selected device because its driver seems not to be installed" ) # disable old driver self.logger.info('Using audio driver "%s"', new_driver.name) if old_driver and old_driver.is_installed(): disabled = old_driver.disable() self.logger.debug( 'Disable previous driver "%s": %s', old_driver.name, disabled ) if not disabled: raise CommandError("Unable to disable current driver") # enable new driver self.logger.debug('Enable new driver "%s"', new_driver.name) driver_enabled = new_driver.enable() if not driver_enabled or not new_driver.is_card_enabled(): self.logger.info( "Unable to enable selected driver. Revert re-enabling previous driver" ) if old_driver: old_driver.enable() raise CommandError("Unable to enable selected device") # everything is fine, save new driver self._set_config_field("driver", new_driver.name) # restart cleep self.send_command("restart_cleep", "system")
def add(self, name, gpio, inverted): """ Return sensor data to add. Can perform specific stuff Args: name (string): sensor name gpio (string): used gpio inverted (bool): True if gpio is inverted Returns: dict: sensor data to add:: { gpios (list): list of gpios data to add sensors (list): list sensors data to add } """ # get assigned gpios assigned_gpios = self._get_assigned_gpios() # check values self._check_parameters([ { 'name': 'name', 'value': name, 'type': str, 'validator': lambda val: self._search_device("name", name) is None, 'message': 'Name "%s" is already used' % name, }, { 'name': 'gpio', 'value': gpio, 'type': str, 'validator': lambda val: gpio not in assigned_gpios, 'message': 'Gpio "%s" is already used' % gpio, }, { 'name': 'inverted', 'value': inverted, 'type': bool }, ]) # TODO add new validator in cleep v0.0.27 if gpio not in self.raspi_gpios: raise InvalidParameter( 'Gpio "%s" does not exist for this raspberry pi' % gpio) # configure gpio gpio_data = { "name": name + "_motion", "gpio": gpio, "mode": "input", "keep": False, "inverted": inverted, } sensor_data = { "name": name, "gpios": [], "type": self.TYPE_MOTION, "subtype": self.SUBTYPE, "on": False, "inverted": inverted, "lastupdate": 0, "lastduration": 0, } # read current gpio value resp = self.send_command("is_gpio_on", "gpios", {"gpio": gpio}) if not resp.error: sensor_data["on"] = resp.data sensor_data["lastupdate"] = int(time.time()) return { "gpios": [ gpio_data, ], "sensors": [ sensor_data, ], }
def reserve_gpio(self, name, gpio, usage, command_sender): """ Reserve a gpio used to configure raspberry pi (ie onewire, lirc...) This action only flag this gpio as reserved to avoid using it again Args: name (string): name of gpio gpio (string) : gpio value usage (string) : describe gpio usage command_sender (string): command request sender (used to set gpio in readonly mode) Returns: dict: Created gpio device Raises: CommandError MissingParameter InvalidParameter """ # fix command_sender: rpcserver is the default gpio entry point if command_sender == "rpcserver": command_sender = "gpios" # check values if gpio: found_gpio = self._search_device("gpio", gpio) if found_gpio is not None and found_gpio["subtype"] != usage: raise InvalidParameter( 'Gpio "%s" is already reserved for "%s" usage' % (found_gpio["gpio"], found_gpio["subtype"]) ) if found_gpio is not None and found_gpio["subtype"] == usage: return found_gpio self._check_parameters( [ { "name": "name", "value": name, "type": str, "validator": lambda val: self._search_device("name", val) is None, "message": 'Name "%s" is already used' % name, }, { "name": "gpio", "value": gpio, "type": str, "validator": lambda val: val in self.get_raspi_gpios().keys(), "message": 'Gpio "%s" does not exist for this raspberry pi' % gpio, }, {"name": "usage", "value": usage, "type": str}, ] ) # gpio is valid, prepare new entry data = { "name": name, "mode": self.MODE_RESERVED, "pin": self.get_raspi_gpios()[gpio], "gpio": gpio, "keep": False, "on": False, "inverted": False, "owner": command_sender, "type": "gpio", "subtype": usage, } # add device self.logger.debug("data=%s" % data) device = self._add_device(data) if device is None: raise CommandError("Unable to add device") return device
def update_sensor(self, sensor_uuid, data): """ Update sensor Args: sensor_uuid (string): sensor uuid data (dict): sensor data Returns: list: list of updated sensors """ if not sensor_uuid: raise MissingParameter("Uuid parameter is missing") sensor = self._get_device(sensor_uuid) if sensor is None: raise InvalidParameter('Sensor with uuid "%s" doesn\'t exist' % sensor_uuid) # search addon addon = self._get_addon(sensor["type"], sensor["subtype"]) if addon is None: raise CommandError('Unhandled sensor type "%s-%s"' % (sensor["type"], sensor["subtype"])) sensor_devices = [] gpio_devices = [] try: # prepare data mixing all params from all sensors data["sensor"] = sensor (gpios, sensors) = addon.update(**data).values() if not isinstance(gpios, list) or not isinstance( sensors, list): # pragma: no cover raise Exception( "Invalid gpios or sensors type. Must be a list") # update gpios for gpio in gpios: resp_gpio = self.send_command("update_gpio", "gpios", self._fix_gpio_device(gpio)) if resp_gpio.error: raise CommandError(resp_gpio.message) gpio_devices.append(resp_gpio.data) # update sensors for sensor in sensors: if not self._update_device(sensor["uuid"], sensor): raise CommandError("Unable to update sensor") sensor_devices.append(sensor) # restart sensor task task = addon.get_task(sensor) if task: self._stop_sensor_task(sensor) self._start_sensor_task(task, [sensor]) return sensor_devices except Exception as error: self.logger.exception('Error occured updating sensor "%s": %s' % (sensor_uuid, data)) raise CommandError("Error updating sensor") from error
def delete_sensor(self, sensor_uuid): """ Delete specified sensor Args: sensor_uuid (string): sensor identifier Returns: bool: True if deletion succeed """ if not sensor_uuid: raise MissingParameter("Uuid parameter is missing") sensor = self._get_device(sensor_uuid) if sensor is None: raise InvalidParameter('Sensor with uuid "%s" doesn\'t exist' % sensor_uuid) # search addon addon = self._get_addon(sensor["type"], sensor["subtype"]) if addon is None: raise CommandError('Unhandled sensor type "%s-%s"' % (sensor["type"], sensor["subtype"])) try: # stop task self._stop_sensor_task(sensor) (gpios, sensors) = addon.delete(sensor).values() if not isinstance(gpios, list) or not isinstance( sensors, list): # pragma: no cover raise Exception( "Invalid gpios or sensors type. Must be a list") # unconfigure gpios self.logger.debug("Gpios=%s" % gpios) for gpio in gpios: # is a reserved gpio self.logger.debug('is_reserved_gpio for gpio "%s"' % gpio) resp = self.send_command("is_reserved_gpio", "gpios", {"gpio": gpio["uuid"]}) self.logger.debug("is_reserved_gpio: %s" % resp) if resp.error: raise CommandError(resp.message) reserved_gpio = resp.data # check if we can delete gpio delete_gpio = True if reserved_gpio: # reserved gpio, don't delete it delete_gpio = False elif self._get_gpio_uses(gpio["gpio"]) > 1: # another device is using gpio, do not delete it in gpio module self.logger.info( "More than one sensor is using gpio, disable gpio deletion" ) delete_gpio = False # delete device in gpio module if delete_gpio: self.logger.debug('Delete gpio "%s" from gpios module' % gpio["uuid"]) resp = self.send_command("delete_gpio", "gpios", {"device_uuid": gpio["uuid"]}) if resp.error: self.logger.warning("Gpio can't be deleted: %s" % resp.error) else: self.logger.debug( "Gpio device not deleted because other sensor is using it" ) # delete sensors for sensor in sensors: self._delete_device(sensor["uuid"]) self.logger.debug('Sensor "%s" deleted successfully' % sensor["uuid"]) return True except Exception as error: self.logger.exception('Error occured deleting sensor "%s":' % (sensor_uuid)) raise CommandError("Error deleting sensor") from error