def extra_state_attributes(self) -> dict[str, Any] | None: """Return the state attributes of the device.""" attr = {} if self.vera_device.has_battery: attr[ATTR_BATTERY_LEVEL] = self.vera_device.battery_level if self.vera_device.is_armable: armed = self.vera_device.is_armed attr[ATTR_ARMED] = "True" if armed else "False" if self.vera_device.is_trippable: last_tripped = self.vera_device.last_trip if last_tripped is not None: utc_time = utc_from_timestamp(int(last_tripped)) attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat() else: attr[ATTR_LAST_TRIP_TIME] = None tripped = self.vera_device.is_tripped attr[ATTR_TRIPPED] = "True" if tripped else "False" power = self.vera_device.power if power: attr[ATTR_CURRENT_POWER_W] = convert(power, float, 0.0) energy = self.vera_device.energy if energy: attr[ATTR_CURRENT_ENERGY_KWH] = convert(energy, float, 0.0) attr["Vera Device Id"] = self.vera_device.vera_device_id return attr
def _parse_volume(cls, player_data): """Parse volume data to determine volume levels and mute state.""" volume = { "level": 0, "step": 0, "muted": False, } try: volume_data = player_data["volume"] volume_muted = volume_data["is_muted"] volume_step = convert(volume_data["step"], int, 0) if volume_data["type"] == "db": level = convert(volume_data["value"], float, 0.0) / 80 * 100 + 100 else: level = convert(volume_data["value"], float, 0.0) volume_level = convert(level, int, 0) / 100 except KeyError: # catch KeyError pass else: volume["muted"] = volume_muted volume["step"] = volume_step volume["level"] = volume_level return volume
def _parse_now_playing(self, player_data): """Parse now playing data to determine title, artist, position, duration and artwork.""" now_playing = { "title": None, "artist": None, "album": None, "position": 0, "duration": 0, "image": None, } now_playing_data = None try: now_playing_data = player_data["now_playing"] media_title = now_playing_data["three_line"]["line1"] media_artist = now_playing_data["three_line"]["line2"] media_album_name = now_playing_data["three_line"]["line3"] media_position = convert(now_playing_data["seek_position"], int, 0) media_duration = convert(now_playing_data.get("length"), int, 0) image_id = now_playing_data.get("image_key") except KeyError: # catch KeyError pass else: now_playing["title"] = media_title now_playing["artist"] = media_artist now_playing["album"] = media_album_name now_playing["position"] = media_position now_playing["duration"] = media_duration if image_id: now_playing["image"] = self._server.roonapi.get_image(image_id) return now_playing
def closest(opp, *args): """Find closest entity. Closest to home: closest(states) closest(states.device_tracker) closest('group.children') closest(states.group.children) Closest to a point: closest(23.456, 23.456, 'group.children') closest('zone.school', 'group.children') closest(states.zone.school, 'group.children') As a filter: states | closest states.device_tracker | closest ['group.children', states.device_tracker] | closest 'group.children' | closest(23.456, 23.456) states.device_tracker | closest('zone.school') 'group.children' | closest(states.zone.school) """ if len(args) == 1: latitude = opp.config.latitude longitude = opp.config.longitude entities = args[0] elif len(args) == 2: point_state = _resolve_state(opp, args[0]) if point_state is None: _LOGGER.warning("Closest:Unable to find state %s", args[0]) return None if not loc_helper.has_location(point_state): _LOGGER.warning( "Closest:State does not contain valid location: %s", point_state) return None latitude = point_state.attributes.get(ATTR_LATITUDE) longitude = point_state.attributes.get(ATTR_LONGITUDE) entities = args[1] else: latitude = convert(args[0], float) longitude = convert(args[1], float) if latitude is None or longitude is None: _LOGGER.warning("Closest:Received invalid coordinates: %s, %s", args[0], args[1]) return None entities = args[2] states = expand(opp, entities) # state will already be wrapped here return loc_helper.closest(latitude, longitude, states)
def distance(opp, *args): """Calculate distance. Will calculate distance from home to a point or between points. Points can be passed in using state objects or lat/lng coordinates. """ locations = [] to_process = list(args) while to_process: value = to_process.pop(0) if isinstance(value, str) and not valid_entity_id(value): point_state = None else: point_state = _resolve_state(opp, value) if point_state is None: # We expect this and next value to be lat&lng if not to_process: _LOGGER.warning( "Distance:Expected latitude and longitude, got %s", value) return None value_2 = to_process.pop(0) latitude = convert(value, float) longitude = convert(value_2, float) if latitude is None or longitude is None: _LOGGER.warning( "Distance:Unable to process latitude and longitude: %s, %s", value, value_2, ) return None else: if not loc_helper.has_location(point_state): _LOGGER.warning( "Distance:State does not contain valid location: %s", point_state) return None latitude = point_state.attributes.get(ATTR_LATITUDE) longitude = point_state.attributes.get(ATTR_LONGITUDE) locations.append((latitude, longitude)) if len(locations) == 1: return opp.config.distance(*locations[0]) return opp.config.units.length( loc_util.distance(*locations[0] + locations[1]), LENGTH_METERS)
def current_power_w(self): """Return the current power usage in W.""" if "power" in self.fibaro_device.properties: power = self.fibaro_device.properties.power if power: return convert(power, float, 0.0) else: return None
def extra_state_attributes(self): """Return the state attributes of the device.""" attr = {"fibaro_id": self.fibaro_device.id} try: if "battery" in self.fibaro_device.interfaces: attr[ATTR_BATTERY_LEVEL] = int( self.fibaro_device.properties.batteryLevel) if "fibaroAlarmArm" in self.fibaro_device.interfaces: attr[ATTR_ARMED] = bool(self.fibaro_device.properties.armed) if "power" in self.fibaro_device.interfaces: attr[ATTR_CURRENT_POWER_W] = convert( self.fibaro_device.properties.power, float, 0.0) if "energy" in self.fibaro_device.interfaces: attr[ATTR_CURRENT_ENERGY_KWH] = convert( self.fibaro_device.properties.energy, float, 0.0) except (ValueError, KeyError): pass return attr
def test_convert(): """Test convert.""" assert util.convert("5", int) == 5 assert util.convert("5", float) == 5.0 assert util.convert("True", bool) is True assert util.convert("NOT A NUMBER", int, 1) == 1 assert util.convert(None, int, 1) == 1 assert util.convert(object, int, 1) == 1
def do_setup(opp, opp_config, config): """Run the setup after we have everything configured.""" # Load calendars the user has configured opp.data[DATA_INDEX] = load_config(opp.config.path(YAML_DEVICES)) calendar_service = GoogleCalendarService(opp.config.path(TOKEN_FILE)) track_new_found_calendars = convert(config.get(CONF_TRACK_NEW), bool, DEFAULT_CONF_TRACK_NEW) setup_services(opp, opp_config, track_new_found_calendars, calendar_service) for calendar in opp.data[DATA_INDEX].values(): discovery.load_platform(opp, "calendar", DOMAIN, calendar, opp_config) # Look for any new calendars opp.services.call(DOMAIN, SERVICE_SCAN_CALENDARS, None) return True
def _update_hum_sensor(state): """Parse humidity sensor value.""" _LOGGER.debug("Updating humidity sensor with value %s", state.state) # Return an error if the sensor change its state to Unknown. if state.state == STATE_UNKNOWN: _LOGGER.error( "Unable to parse humidity sensor %s, state: %s", state.entity_id, state.state, ) return None unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) hum = util.convert(state.state, float) if hum is None: _LOGGER.error( "Unable to parse humidity sensor %s, state: %s", state.entity_id, state.state, ) return None if unit != PERCENTAGE: _LOGGER.error( "Humidity sensor %s has unsupported unit: %s %s", state.entity_id, unit, " (allowed: %)", ) return None if hum > 100 or hum < 0: _LOGGER.error( "Humidity sensor %s is out of range: %s %s", state.entity_id, hum, "(allowed: 0-100%)", ) return None return hum
def setup_platform(opp, config, add_entities, discovery_info=None): """Display the current season.""" if None in (opp.config.latitude, opp.config.longitude): _LOGGER.error("Latitude or longitude not set in Open Peer Power config") return False latitude = util.convert(opp.config.latitude, float) _type = config.get(CONF_TYPE) name = config.get(CONF_NAME) if latitude < 0: hemisphere = SOUTHERN elif latitude > 0: hemisphere = NORTHERN else: hemisphere = EQUATOR _LOGGER.debug(_type) add_entities([Season(opp, hemisphere, _type, name)]) return True
def _update_temp_sensor(state): """Parse temperature sensor value.""" _LOGGER.debug("Updating temp sensor with value %s", state.state) # Return an error if the sensor change its state to Unknown. if state.state == STATE_UNKNOWN: _LOGGER.error( "Unable to parse temperature sensor %s with state: %s", state.entity_id, state.state, ) return None unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) temp = util.convert(state.state, float) if temp is None: _LOGGER.error( "Unable to parse temperature sensor %s with state: %s", state.entity_id, state.state, ) return None # convert to celsius if necessary if unit == TEMP_FAHRENHEIT: return util.temperature.fahrenheit_to_celsius(temp) if unit == TEMP_CELSIUS: return temp _LOGGER.error( "Temp sensor %s has unsupported unit: %s (allowed: %s, %s)", state.entity_id, unit, TEMP_CELSIUS, TEMP_FAHRENHEIT, ) return None
def extra_state_attributes(self): """Return the state attributes of the device.""" attr = {} if self.maker_params: # Is the maker sensor on or off. if self.maker_params["oppensor"]: # Note a state of 1 matches the WeMo app 'not triggered'! if self.maker_params["sensorstate"]: attr[ATTR_SENSOR_STATE] = STATE_OFF else: attr[ATTR_SENSOR_STATE] = STATE_ON # Is the maker switch configured as toggle(0) or momentary (1). if self.maker_params["switchmode"]: attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_MOMENTARY else: attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_TOGGLE if self.insight_params or (self.coffeemaker_mode is not None): attr[ATTR_CURRENT_STATE_DETAIL] = self.detail_state if self.insight_params: attr["on_latest_time"] = WemoSwitch.as_uptime( self.insight_params["onfor"]) attr["on_today_time"] = WemoSwitch.as_uptime( self.insight_params["ontoday"]) attr["on_total_time"] = WemoSwitch.as_uptime( self.insight_params["ontotal"]) attr["power_threshold_w"] = ( convert(self.insight_params["powerthreshold"], float, 0.0) / 1000.0) if self.coffeemaker_mode is not None: attr[ATTR_COFFEMAKER_MODE] = self.coffeemaker_mode return attr
def update(self) -> None: """Update the state.""" super().update() if self.vera_device.category == veraApi.CATEGORY_TEMPERATURE_SENSOR: self.current_value = self.vera_device.temperature vera_temp_units = self.vera_device.vera_controller.temperature_units if vera_temp_units == "F": self._temperature_units = TEMP_FAHRENHEIT else: self._temperature_units = TEMP_CELSIUS elif self.vera_device.category == veraApi.CATEGORY_LIGHT_SENSOR: self.current_value = self.vera_device.light elif self.vera_device.category == veraApi.CATEGORY_UV_SENSOR: self.current_value = self.vera_device.light elif self.vera_device.category == veraApi.CATEGORY_HUMIDITY_SENSOR: self.current_value = self.vera_device.humidity elif self.vera_device.category == veraApi.CATEGORY_SCENE_CONTROLLER: controller = cast(veraApi.VeraSceneController, self.vera_device) value = controller.get_last_scene_id(True) time = controller.get_last_scene_time(True) if time == self.last_changed_time: self.current_value = None else: self.current_value = value self.last_changed_time = time elif self.vera_device.category == veraApi.CATEGORY_POWER_METER: power = convert(self.vera_device.power, float, 0) self.current_value = int(round(power, 0)) elif self.vera_device.is_trippable: tripped = self.vera_device.is_tripped self.current_value = "Tripped" if tripped else "Not Tripped" else: self.current_value = "Unknown"
def current_power_w(self): """Return the current power usage in W.""" if "power" in self.fibaro_device.interfaces: return convert(self.fibaro_device.properties.power, float, 0.0) return None
def today_energy_kwh(self): """Return the today total energy usage in kWh.""" if self.insight_params: miliwatts = convert(self.insight_params["todaymw"], float, 0.0) return round(miliwatts / (1000.0 * 1000.0 * 60), 2)
def current_power_w(self): """Return the current power usage in W.""" if self.insight_params: return convert(self.insight_params["currentpower"], float, 0.0) / 1000.0
def _check_entity_ready(self): """Check if all required values are discovered and create entity.""" if self._workaround_ignore: return if self._entity is not None: return for name in self._schema[const.DISC_VALUES]: if self._values[name] is None and not self._schema[ const.DISC_VALUES][name].get(const.DISC_OPTIONAL): return component = self._schema[const.DISC_COMPONENT] workaround_component = workaround.get_device_component_mapping( self.primary) if workaround_component and workaround_component != component: if workaround_component == workaround.WORKAROUND_IGNORE: _LOGGER.info( "Ignoring Node %d Value %d due to workaround", self.primary.node.node_id, self.primary.value_id, ) # No entity will be created for this value self._workaround_ignore = True return _LOGGER.debug("Using %s instead of %s", workaround_component, component) component = workaround_component entity_id = self._registry.async_get_entity_id( component, DOMAIN, compute_value_unique_id(self._node, self.primary)) if entity_id is None: value_name = _value_name(self.primary) entity_id = generate_entity_id(component + ".{}", value_name, []) node_config = self._device_config.get(entity_id) # Configure node _LOGGER.debug( "Adding Node_id=%s Generic_command_class=%s, " "Specific_command_class=%s, " "Command_class=%s, Value type=%s, " "Genre=%s as %s", self._node.node_id, self._node.generic, self._node.specific, self.primary.command_class, self.primary.type, self.primary.genre, component, ) if node_config.get(CONF_IGNORED): _LOGGER.info("Ignoring entity %s due to device settings", entity_id) # No entity will be created for this value self._workaround_ignore = True return polling_intensity = convert(node_config.get(CONF_POLLING_INTENSITY), int) if polling_intensity: self.primary.enable_poll(polling_intensity) platform = import_module(f".{component}", __name__) device = platform.get_device(node=self._node, values=self, node_config=node_config, opp=self._opp) if device is None: # No entity will be created for this value self._workaround_ignore = True return self._entity = device @callback def _on_ready(sec): _LOGGER.info( "Z-Wave entity %s (node_id: %d) ready after %d seconds", device.name, self._node.node_id, sec, ) self._opp.async_add_job(discover_device, component, device) @callback def _on_timeout(sec): _LOGGER.warning( "Z-Wave entity %s (node_id: %d) not ready after %d seconds, " "continuing anyway", device.name, self._node.node_id, sec, ) self._opp.async_add_job(discover_device, component, device) async def discover_device(component, device): """Put device in a dictionary and call discovery on it.""" if self._opp.data[DATA_DEVICES].get(device.unique_id): return self._opp.data[DATA_DEVICES][device.unique_id] = device if component in PLATFORMS: async_dispatcher_send(self._opp, f"zwave_new_{component}", device) else: await discovery.async_load_platform( self._opp, component, DOMAIN, {const.DISCOVERY_DEVICE: device.unique_id}, self._zwave_config, ) if device.unique_id: self._opp.add_job(discover_device, component, device) else: self._opp.add_job(check_has_unique_id, device, _on_ready, _on_timeout)
def _finalize_start(): """Perform final initializations after Z-Wave network is awaked.""" polling_interval = convert(config.get(CONF_POLLING_INTERVAL), int) if polling_interval is not None: network.set_poll_interval(polling_interval, False) poll_interval = network.get_poll_interval() _LOGGER.info("Z-Wave polling interval set to %d ms", poll_interval) opp.bus.listen_once(EVENT_OPENPEERPOWER_STOP, stop_network) # Register node services for Z-Wave network opp.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node) opp.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE, add_node_secure) opp.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node) opp.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND, cancel_command) opp.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK, heal_network) opp.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset) opp.services.register(DOMAIN, const.SERVICE_TEST_NETWORK, test_network) opp.services.register(DOMAIN, const.SERVICE_STOP_NETWORK, stop_network) opp.services.register(DOMAIN, const.SERVICE_RENAME_NODE, rename_node, schema=RENAME_NODE_SCHEMA) opp.services.register(DOMAIN, const.SERVICE_RENAME_VALUE, rename_value, schema=RENAME_VALUE_SCHEMA) opp.services.register( DOMAIN, const.SERVICE_SET_CONFIG_PARAMETER, set_config_parameter, schema=SET_CONFIG_PARAMETER_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_SET_NODE_VALUE, set_node_value, schema=SET_NODE_VALUE_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_REFRESH_NODE_VALUE, refresh_node_value, schema=REFRESH_NODE_VALUE_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_PRINT_CONFIG_PARAMETER, print_config_parameter, schema=PRINT_CONFIG_PARAMETER_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_REMOVE_FAILED_NODE, remove_failed_node, schema=NODE_SERVICE_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_REPLACE_FAILED_NODE, replace_failed_node, schema=NODE_SERVICE_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_CHANGE_ASSOCIATION, change_association, schema=CHANGE_ASSOCIATION_SCHEMA, ) opp.services.register(DOMAIN, const.SERVICE_SET_WAKEUP, set_wakeup, schema=SET_WAKEUP_SCHEMA) opp.services.register(DOMAIN, const.SERVICE_PRINT_NODE, print_node, schema=NODE_SERVICE_SCHEMA) opp.services.register( DOMAIN, const.SERVICE_REFRESH_ENTITY, async_refresh_entity, schema=REFRESH_ENTITY_SCHEMA, ) opp.services.register(DOMAIN, const.SERVICE_REFRESH_NODE, refresh_node, schema=NODE_SERVICE_SCHEMA) opp.services.register( DOMAIN, const.SERVICE_RESET_NODE_METERS, reset_node_meters, schema=RESET_NODE_METERS_SCHEMA, ) opp.services.register( DOMAIN, const.SERVICE_SET_POLL_INTENSITY, set_poll_intensity, schema=SET_POLL_INTENSITY_SCHEMA, ) opp.services.register(DOMAIN, const.SERVICE_HEAL_NODE, heal_node, schema=HEAL_NODE_SCHEMA) opp.services.register(DOMAIN, const.SERVICE_TEST_NODE, test_node, schema=TEST_NODE_SCHEMA)
def current_power_w(self) -> float | None: """Return the current power usage in W.""" power = self.vera_device.power if power: return convert(power, float, 0.0)
def today_energy_kwh(self): """Return the today total energy usage in kWh.""" if "energy" in self.fibaro_device.interfaces: return convert(self.fibaro_device.properties.energy, float, 0.0) return None