def begin(self, username, password, clientid, hostname='mqtt.mydevices.com', port=8883): """Initializes the client and connects to Cayenne. username is the Cayenne username. password is the Cayenne password. clientid is the Cayennne client ID for the device. hostname is the MQTT broker hostname. port is the MQTT broker port. """ self.root_topic = 'v1/{}/things/{}'.format(username, clientid) self.client = mqtt.Client(client_id=clientid, clean_session=True, userdata=self) self.client.on_connect = self.connect_callback self.client.on_disconnect = self.disconnect_callback self.client.on_message = self.message_callback self.client.username_pw_set(username, password) if port != 1883: self.client.tls_set(ca_certs='/etc/ssl/certs/ca-certificates.crt', tls_version=PROTOCOL_TLSv1_2) self.client.connect(hostname, port, 60) info('Connecting to {}:{}'.format(hostname, port))
def connect_callback(self, client, userdata, flags, rc): """The callback for when the client connects to the server. client is the client instance for this callback. userdata is the private user data as set in Client() or userdata_set(). flags are the response flags sent by the broker. rc is the connection result. """ if rc != 0: # MQTT broker error codes broker_errors = { 1: 'unacceptable protocol version', 2: 'identifier rejected', 3: 'server unavailable', 4: 'bad user name or password', 5: 'not authorized', } raise Exception("Connection failed, " + broker_errors.get(rc, "result code " + str(rc))) else: info("Connected with result code " + str(rc)) self.connected = True # Subscribing in on_connect() means that if we lose the connection and # reconnect then subscriptions will be renewed. client.subscribe(self.get_topic_string(COMMAND_TOPIC, True)) client.subscribe(self.get_topic_string(COMMAND_JSON_TOPIC, False))
def run(self): """Run the job and immediately reschedule it.""" debug('Run job') if self.unit == 'date': if self.last_run is not None: info('date job can run only once. Last run: ' + str(self.last_run)) return CancelJob now = datetime.utcnow() #If more than the grace period has passed since the scheduled start time we cancel the job if now > (self.at_time + self.grace_period): info('Job scheduled time has passed, job will not be run: ' + str(self.at_time) + ' current time: ' + str(now)) return CancelJob debug('Running job: {}'.format(self)) if self.end_date is not None: if datetime.utcnow() > self.end_date: info('Skipping job, end date has passed: ' + str(self.end_date)) return CancelJob ret = self.job_func() self.last_run = datetime.utcnow() if self.unit == 'date': info('Date job finished, it will not be recheduled') return CancelJob self._schedule_next_run() info('Job finished, next run time: ' + str(self.next_run)) return ret
def writeByte(self, value): try: info('DS2408 writeByte {} {} {}'.format(self.slave, value, bytearray([value]))) command = 'sudo python3 -m myDevices.devices.writevalue /sys/bus/w1/devices/{}/output {}'.format(self.slave, value) subprocess.call(command.split()) except Exception as ex: error('DS2408 writeByte error: {}'.format(ex))
def ProcessPowerCommand(self, message): """Process command to reboot/shutdown the system Returns: True if command was processed, False otherwise.""" error_message = None try: self.EnqueueCommandResponse(message, error_message) commands = { cayennemqtt.SYS_POWER_RESET: 'sudo shutdown -r now', cayennemqtt.SYS_POWER_HALT: 'sudo shutdown -h now' } if int(message['payload']) == 1: debug('Processing power command') data = [] cayennemqtt.DataChannel.add(data, message['channel'], value=1) self.EnqueuePacket(data) self.writeQueue.join() info('Calling execute: {}'.format( commands[message['channel']])) output, result = executeCommand(commands[message['channel']]) debug('ProcessPowerCommand: {}, result: {}, output: {}'.format( message, result, output)) if result != 0: error_message = 'Error executing shutdown command' except Exception as ex: error_message = '{}: {}'.format(type(ex).__name__, ex) if error_message: error(error_message) data = [] cayennemqtt.DataChannel.add(data, message['channel'], value=0) self.EnqueuePacket(data) raise ExecuteMessageError(error_message) return error_message == None
def testSensorsInfo(self): debug('testSensorsInfo') sensors = SensorsClientTest.client.SensorsInfo() info('Sensors info: {}'.format(sensors)) for sensor in sensors: self.assertEqual('dev:', sensor['channel'][:4]) self.assertIn('value', sensor)
def RemoveSensor(self, name): """Remove an existing sensor/actuator Args: name: Name of sensor to remove Returns: True for success, False otherwise. """ bVal = False try: if self.pluginManager.is_plugin(name): return self.pluginManager.disable(name) sensorRemove = name try: sensor = instance.deviceInstance(sensorRemove) if hasattr(sensor, 'removeCallback'): sensor.removeCallback() except: pass with self.sensorMutex: retValue = manager.removeDevice(sensorRemove) info('Remove device returned: {}'.format(retValue)) if retValue[0] == 200: bVal = True except: exception("Remove sensor failed") bVal = False return bVal
def EnableSensor(self, sensor, enable): """Enable a sensor/actuator Args: sensor: Hash composed from name and device class/type enable: 1 to enable, 0 to disable Returns: True for success, False otherwise. """ info('Enable sensor: ' + str(sensor) + ' ' + str(enable)) try: if sensor is None: return False if enable is None: return False with self.sensorMutex: if enable == 0: #add item to the list if sensor not in self.disabledSensors: DbManager.Insert(self.disabledSensorTable, sensor) self.disabledSensors[sensor] = 1 else: #remove item from the list if sensor in self.disabledSensors: DbManager.Delete(self.disabledSensorTable, sensor) del self.disabledSensors[sensor] #save list except Exception as ex: error('EnableSensor Failed with exception: ' + str(ex)) return False return True
def EditSensor(self, name, description, device, args): """Edit an existing sensor/actuator Args: name: Name of sensor to edit description: New sensor description device: New sensor device class args: New sensor specific args Returns: True for success, False otherwise. """ info('EditSensor: {}, {}, {}, {}'.format(name, description, device, args)) bVal = False try: sensorEdit = {} name = name sensorEdit['name'] = name sensorEdit['device'] = device sensorEdit['description'] = description sensorEdit['args'] = args with self.sensorMutex: retValue = manager.updateDevice(name, sensorEdit) self.InitCallbacks() info('Edit device returned: {}'.format(retValue)) if retValue[0] == 200: bVal = True except: exception("Edit sensor failed") bVal = False return bVal
def AddSensor(self, name, description, device, args): """Add a new sensor/actuator Args: name: Name of sensor to add description: Sensor description device: Sensor device class args: Sensor specific args Returns: True for success, False otherwise. """ info('AddSensor: {}, {}, {}, {}'.format(name, description, device, args)) bVal = False try: sensorAdd = {} if name: sensorAdd['name'] = name if device: sensorAdd['device'] = device if args: sensorAdd['args'] = args if description: sensorAdd['description'] = description with self.sensorMutex: retValue = manager.addDeviceJSON(sensorAdd) self.InitCallbacks() info('Add device returned: {}'.format(retValue)) if retValue[0] == 200: bVal = True except Exception: exception('Error adding sensor') bVal = False return bVal
def CalculateAverages(self, current_avgs, new_sample, count, count_sensor): info('History CalculateAverages increment: ' + str(count)) count += 1 self.CalculateNetworkSpeedAverages(current_avgs, new_sample, count) self.CalculateSystemInfoAverages(current_avgs, new_sample, count) self.CalculateSensorsInfoAverages(current_avgs, new_sample, count_sensor) return count
def GpioCommand(self, command, channel, value): """Execute onboard GPIO command Args: command: Type of command to execute channel: GPIO pin value: Value to use for writing data Returns: String containing command specific return value on success, or 'failure' on failure """ info('GpioCommand {}, channel {}, value {}'.format(command, channel, value)) result = 'failure' if command == 'function': old_state = self.gpio.digitalRead(channel) if value.lower() in ('in', 'input'): result = str(self.gpio.setFunctionString(channel, 'in')) elif value.lower() in ('out', 'output'): result = str(self.gpio.setFunctionString(channel, 'out')) new_state = self.gpio.digitalRead(channel) if new_state != old_state: self.OnGpioStateChange(channel, new_state) elif command in ('value', ''): return self.gpio.digitalWrite(channel, int(value)) debug('GPIO command failed') return result
def run(self): """Send messages to the server until the thread is stopped""" debug('WriterThread run') while self.Continue: try: if self.cloudClient.exiting.wait(GENERAL_SLEEP_THREAD): return if self.cloudClient.mqttClient.connected == False: info('WriterThread mqttClient not connected') continue got_packet = False topic, message = self.cloudClient.DequeuePacket() if topic or message: got_packet = True try: if message or topic == cayennemqtt.JOBS_TOPIC: # debug('WriterThread, topic: {} {}'.format(topic, message)) if not isinstance(message, str): message = dumps(message) self.cloudClient.mqttClient.publish_packet( topic, message) message = None except: exception("WriterThread publish packet error") finally: if got_packet: self.cloudClient.writeQueue.task_done() except: exception("WriterThread unexpected error") return
def ProcessDeviceCommand(self, message): """Process a device command to add/edit/remove a sensor Returns: True if command was processed, False otherwise.""" error = None try: payload = message['payload'] info('ProcessDeviceCommand payload: {}'.format(payload)) if message['suffix'] == 'add': result = self.sensorsClient.AddSensor(payload['sensorId'], payload['description'], payload['class'], payload['args']) elif message['suffix'] == 'edit': result = self.sensorsClient.EditSensor(payload['sensorId'], payload['description'], payload['class'], payload['args']) elif message['suffix'] == 'delete': result = self.sensorsClient.RemoveSensor(payload['sensorId']) else: error = 'Unknown device command: {}'.format(message['suffix']) debug('ProcessDeviceCommand result: {}'.format(result)) if result is False: error = 'Device command failed' except Exception as ex: error = '{}: {}'.format(type(ex).__name__, ex) self.EnqueueCommandResponse(message, error) return error == None
def testStorageInfo(self): storage_info = self.info['Storage'] info(storage_info) self.assertEqual(set(storage_info.keys()), set(('list', ))) for item in storage_info['list']: self.assertLessEqual(set(('device', 'filesystem', 'mount')), set(item.keys()))
def GpioCommand(self, commandType, method, channel, value): debug('') info('GpioCommand ' + commandType + ' method ' + method + ' Channel: ' + str(channel) + ' Value: ' + str(value)) if commandType == 'function': if method == 'POST': debug('setFunction:' + str(channel) + ' ' + str(value)) return str(self.gpio.setFunctionString(channel, value)) if method == 'GET': debug('getFunction:' + str(channel) + ' ' + str(value)) return str(self.gpio.getFunctionString(channel)) if commandType == 'value': if method == 'POST': debug('digitalWrite:' + str(channel) + ' ' + str(value)) retVal = str(self.gpio.digitalWrite(channel, value)) return retVal if method == 'GET': debug('digitalRead:' + str(channel)) return str(self.gpio.digitalRead(channel)) if commandType == 'integer': if method == 'POST': debug('portWrite:' + str(value)) return str(self.gpio.portWrite(value)) if method == 'GET': debug('portRead:') return str(self.gpio.portRead()) debug.log('GpioCommand not set') return 'failure'
def Start(self): #debug('Start') if self.connected: ret = False error('Start already connected') else: info('Connecting to: {}:{}'.format(self.HOST, self.PORT)) count = 0 with self.mutex: count += 1 while self.connected == False and count < 30: try: self.sock = None self.wrappedSocket = None ##debug('Start wrap_socket') self.sock = socket(AF_INET, SOCK_STREAM) #self.wrappedSocket = wrap_socket(self.sock, ca_certs="/etc/myDevices/ca.crt", cert_reqs=CERT_REQUIRED) self.wrappedSocket = wrap_socket(self.sock) self.wrappedSocket.connect((self.HOST, self.PORT)) info('myDevices cloud connected') self.connected = True except socket_error as serr: Daemon.OnFailure('cloud', serr.errno) error('Start failed: ' + str(self.HOST) + ':' + str(self.PORT) + ' Error:' + str(serr)) self.connected = False sleep(30 - count) return self.connected
def AddScheduledItem(self, jsonData, insert=False): #add schedule to both memory and db debug('') retVal = False try: scheduleItem = ScheduleItem(jsonData) if scheduleItem.id is None: raise ValueError( 'No id specified for scheduled item: {}'.format(jsonData)) with self.mutex: try: if scheduleItem.id not in self.schedules: if insert == True: self.AddDbItem(scheduleItem.id, ToJson(jsonData)) info('Setup item: ' + str(scheduleItem.to_dict())) retVal = self.Setup(scheduleItem) if retVal == True: self.schedules[scheduleItem.id] = scheduleItem else: retVal = self.UpdateScheduledItem(jsonData) except: exception('Error adding scheduled item') except: exception('AddScheduledItem Failed') return retVal
def EnableSensor(self, sensor, enable): #sensor is the hash composed from name and device class/type info('Enable sensor: ' + str(sensor) + ' ' + str(enable)) try: if sensor is None: return False if enable is None: return False with self.sensorMutex: if enable == 0: #add item to the list if sensor not in self.disabledSensors: rowId = DbManager.Insert(self.disabledSensorTable, sensor) self.disabledSensors[sensor] = 1 else: #remove item from the list if sensor in self.disabledSensors: DbManager.Delete(self.disabledSensorTable, sensor) del self.disabledSensors[sensor] #save list except Exception as ex: error('EnableSensor Failed with exception: ' + str(ex)) return False self.AddRefresh() return True
def addDeviceJSON(json): if "name" in json: name = json["name"] else: name = "%X" % time() device = json["device"] if 'args' in json: args = json["args"] else: args = {} if 'index' in json: index = json["index"] else: index = 99 if 'description' in json: description = json["description"] else: description = name info("{} {} {} {} {} {}".format(name,index, device, description, args, "rest")) res = addDevice(name, device, description, args, "rest") sleep(0.1) logger.debug('Now saving device') if res == 1: saveDevice(name, int(time())) return (200, "OK", "text/plain") elif res == -1: return (409, "ALREADY_EXISTS", "text/plain") elif res == 0: return (500, "ERROR", "text/plain")
def ExecuteMessage(self, message): """Execute an action described in a message object Returns: True if action was executed, False otherwise.""" result = False if not message: return result channel = message['channel'] info('ExecuteMessage: {}'.format(message)) if channel in (cayennemqtt.SYS_POWER_RESET, cayennemqtt.SYS_POWER_HALT): result = self.ProcessPowerCommand(message) elif channel.startswith(cayennemqtt.DEV_SENSOR): result = self.ProcessSensorCommand(message) elif channel.startswith(cayennemqtt.SYS_GPIO): result = self.ProcessGpioCommand(message) elif channel == cayennemqtt.AGENT_DEVICES: result = self.ProcessDeviceCommand(message) elif channel in (cayennemqtt.SYS_I2C, cayennemqtt.SYS_SPI, cayennemqtt.SYS_UART, cayennemqtt.SYS_ONEWIRE): result = self.ProcessConfigCommand(message) elif channel == cayennemqtt.AGENT_MANAGE: result = self.ProcessAgentCommand(message) elif channel == cayennemqtt.AGENT_SCHEDULER: result = self.ProcessSchedulerCommand(message) else: info('Unknown message') return result
def write(self, value): """Logs the specified value.""" self.value = value if self.digital: info('Example actuator digital value: {}'.format(self.value)) else: info('Example actuator analog value: {}'.format(self.value))
def run(self): """Send messages to the server until the thread is stopped""" debug('WriterThread run') while self.Continue: try: if self.cloudClient.exiting.wait(GENERAL_SLEEP_THREAD): return if self.cloudClient.mqttClient.connected == False: info('WriterThread mqttClient not connected') continue got_packet = False topic, message = self.cloudClient.DequeuePacket() info("topic={}, message={}".format(topic, message)) # startswith(cayennemqtt.DEV_SENSOR) if topic or message: got_packet = True try: if message or topic == cayennemqtt.JOBS_TOPIC: if topic == cayennemqtt.DATA_TOPIC: for sensor in message: if sensor['channel'].startswith( cayennemqtt.DEV_SENSOR): # "state_topic": "{}/sensor/{}/dev:{}/state".format(self.mqtt_dis_prefix, self.serial, name ), state_topic = "sensor/{}/{}/state".format( self.cloudClient.hardware.Serial, sensor['channel']) sensor_message = { "domain": 'sensor', "device_class": sensor['type'], "location": "", 'value': sensor['value'] } sensor_message = dumps(sensor_message) info("state_topic={} ,sensor_message={}". format(state_topic, sensor_message)) self.cloudClient.mqttClient.publish_packet( state_topic, sensor_message) continue elif sensor['channel'].startswith( cayennemqtt.SYS_GPIO): info("sys.gpio={} ".format(sensor)) elif sensor['channel'].startswith("sys"): info("sys={} ".format(sensor)) if not isinstance(message, str): message = dumps(message) self.cloudClient.mqttClient.publish_packet( topic, message) message = None except: exception("WriterThread publish packet error") finally: if got_packet: self.cloudClient.writeQueue.task_done() except: exception("WriterThread unexpected error") return
def testNetworkInfo(self): network_info = self.info['Network'] info(network_info) self.assertGreaterEqual(set(network_info.keys()), set(('list', ))) for key, value in network_info['list'].items(): self.assertTrue(value) self.assertLessEqual(set(value.keys()), set(('ip', 'ipv6', 'mac'))) self.assertIsNotNone(network_info['list']['eth0']['ip']['address'])
def loadModules(bus): if BUSLIST[bus]["enabled"] == False and not modulesLoaded(bus): info("Loading %s modules" % bus) for module in BUSLIST[bus]["modules"]: loadModule(module) if "wait" in BUSLIST[bus]: info("Sleeping %ds to let %s modules load" % (BUSLIST[bus]["wait"], bus)) time.sleep(BUSLIST[bus]["wait"])
def setUp(self): setInfo() system_info = SystemInfo() self.info = { item['channel']: item for item in system_info.getSystemInformation() } info(self.info)
def signal_handler(signal, frame): if client: if signal == SIGINT: info('Program interrupt received, client exiting') client.Destroy() remove(pidfile) else: client.Restart()
def missingOneWireDevice(device): if device['class'] in FAMILIES.values() and ( 'slave' not in device['args'] or not deviceExists(device['args']['slave'])): logger.info('1-wire device does not exist: {}, {}'.format( device['class'], device['args']['slave'])) return True return False
def __init__(self, client): """Initialize the bus and sensor info and start monitoring sensor states""" self.cloudClient = client self.sensorMutex = RLock() self.realTimeMutex = RLock() self.exiting = Event() self.onDataChanged = None self.systemData = [] self.currentSystemState = [] self.currentRealTimeData = {} self.queuedRealTimeData = {} self.disabledSensors = {} self.disabledSensorTable = "disabled_sensors" checkAllBus() self.gpio = GPIO() # self.downloadSpeed = DownloadSpeed(Config(APP_SETTINGS)) # self.downloadSpeed.getDownloadSpeed() manager.addDeviceInstance("GPIO", "GPIO", "GPIO", self.gpio, [], "system") manager.loadJsonDevices("rest") if not DYNAMIC_DEVICES: warn("loadJsonDevices is None") for sensor in sensors.values(): # info('--------{} {} {}'.format(sensor['name'], sensor['description'], sensor['device'])) self.AddSensor(sensor['name'], sensor['description'], sensor['device'], sensor['args']) # # info(DYNAMIC_DEVICES) self.config = Config(APP_SETTINGS) self.clientId = self.config.get('Agent', 'ClientID', None) self.mqtt_dis_prefix = self.config.get('Agent', 'MQTT_DIS_PREFIX', "homeassistant") self.serial = self.cloudClient.hardware.Serial for name, device in DYNAMIC_DEVICES.items(): for type in device['type']: if type in ['DAC', 'ADC']: continue topic, message = self.AddMQTTSensorDevice(name, type, device) if self.cloudClient: info("{} {}".format(topic, message)) self.cloudClient.EnqueuePacket(message, topic) # info(mqttsensor) results = DbManager.Select(self.disabledSensorTable) if results: for row in results: self.disabledSensors[row[0]] = 1 self.realTimeMonitorRunning = False self.pluginManager = PluginManager(self.OnPluginChange) self.pluginManager.load_plugins() self.InitCallbacks() self.StartMonitoring()
def Restart(): try: info('Daemon Restarting myDevices' ) (output, returncode) = ServiceManager.ExecuteCommand('sudo service myDevices restart') debug(str(output) + ' ' + str(returncode)) del output except: exception ("Daemon::Restart Unexpected error") Daemon.Exit()