def test_update_with_application_xml_convert_json_attrs_with_jsonattr_template( self, ): """Test attributes get extracted from a JSON result that was converted from XML with application/xml mime type.""" json_attrs_path = "$.main" value_template = template("{{ value_json.main.dog }}") value_template.hass = self.hass self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect( "<main><dog>1</dog><cat>3</cat></main>", CaseInsensitiveDict({"Content-Type": "application/xml"}), ), ) self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, value_template, ["dog", "cat"], self.force_update, self.resource_template, json_attrs_path, ) self.sensor.update() assert "3" == self.sensor.device_state_attributes["cat"] assert "1" == self.sensor.device_state_attributes["dog"] assert "1" == self.sensor.state
def test_update_with_xml_convert_json_attrs_with_json_attrs_path(self): """Test attributes get extracted from a JSON result that was converted from XML with a template for the attributes.""" json_attrs_path = "$.toplevel.second_level" value_template = template("{{ value_json.toplevel.master_value }}") value_template.hass = self.hass self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect( "<toplevel><master_value>master</master_value><second_level><some_json_key>some_json_value</some_json_key><some_json_key2>some_json_value2</some_json_key2></second_level></toplevel>", CaseInsensitiveDict({"Content-Type": "text/xml+svg"}), ), ) self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, value_template, ["some_json_key", "some_json_key2"], self.force_update, self.resource_template, json_attrs_path, ) self.sensor.update() assert "some_json_value" == self.sensor.device_state_attributes[ "some_json_key"] assert ("some_json_value2" == self.sensor.device_state_attributes["some_json_key2"]) assert "master" == self.sensor.state
def test_update_with_xml_convert_json_attrs_with_jsonattr_template(self): """Test attributes get extracted from a JSON result that was converted from XML.""" json_attrs_path = "$.response" value_template = template("{{ value_json.response.bss.wlan }}") value_template.hass = self.hass self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect( '<?xml version="1.0" encoding="utf-8"?><response><scan>0</scan><ver>12556</ver><count>48</count><ssid>alexander</ssid><bss><valid>0</valid><name>0</name><privacy>0</privacy><wlan>bogus</wlan><strength>0</strength></bss><led0>0</led0><led1>0</led1><led2>0</led2><led3>0</led3><led4>0</led4><led5>0</led5><led6>0</led6><led7>0</led7><btn0>up</btn0><btn1>up</btn1><btn2>up</btn2><btn3>up</btn3><pot0>0</pot0><usr0>0</usr0><temp0>0x0XF0x0XF</temp0><time0> 0</time0></response>', CaseInsensitiveDict({"Content-Type": "text/xml"}), ), ) self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, value_template, ["led0", "led1", "temp0", "time0", "ver"], self.force_update, self.resource_template, json_attrs_path, ) self.sensor.update() assert "0" == self.sensor.device_state_attributes["led0"] assert "0" == self.sensor.device_state_attributes["led1"] assert "0x0XF0x0XF" == self.sensor.device_state_attributes["temp0"] assert "0" == self.sensor.device_state_attributes["time0"] assert "12556" == self.sensor.device_state_attributes["ver"] assert "bogus" == self.sensor.state
def setUp(self): """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.initial_state = "initial_state" self.rest = Mock("rest.RestData") self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect('{ "key": "' + self.initial_state + '" }'), ) self.name = "foo" self.unit_of_measurement = "MB" self.device_class = None self.value_template = template("{{ value_json.key }}") self.value_template.hass = self.hass self.force_update = False self.resource_template = None self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, self.value_template, [], self.force_update, self.resource_template, )
def test_update_with_xml_convert_bad_xml(self, mock_logger): """Test attributes get extracted from a XML result with bad xml.""" value_template = template("{{ value_json.toplevel.master_value }}") value_template.hass = self.hass self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect( "this is not xml", CaseInsensitiveDict({"Content-Type": "text/xml"})), ) self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, value_template, ["key"], self.force_update, self.resource_template, self.json_attrs_path, ) self.sensor.update() assert {} == self.sensor.device_state_attributes assert mock_logger.warning.called assert mock_logger.debug.called
def test_update_with_failed_get(self, mock_logger): """Test attributes get extracted from a XML result with bad xml.""" value_template = template("{{ value_json.toplevel.master_value }}") value_template.hass = self.hass self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect(None, None), ) self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, value_template, ["key"], self.force_update, self.resource_template, self.json_attrs_path, ) self.sensor.update() assert {} == self.sensor.device_state_attributes assert mock_logger.warning.called assert mock_logger.debug.called assert self.sensor.state is None assert self.sensor.available is False
def setUp(self): """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() self.initial_state = "initial_state" self.rest = Mock("rest.RestData") self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect( '{ "key": "' + self.initial_state + '" }', CaseInsensitiveDict({"Content-Type": CONTENT_TYPE_JSON}), ), ) self.name = "foo" self.unit_of_measurement = DATA_MEGABYTES self.device_class = None self.value_template = template("{{ value_json.key }}") self.json_attrs_path = None self.value_template.hass = self.hass self.force_update = False self.resource_template = None self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, self.value_template, [], self.force_update, self.resource_template, self.json_attrs_path, ) self.addCleanup(self.hass.stop)
def test_update_with_json_attrs_with_json_attrs_path(self): """Test attributes get extracted from a JSON result with a template for the attributes.""" json_attrs_path = "$.toplevel.second_level" value_template = template("{{ value_json.toplevel.master_value }}") value_template.hass = self.hass self.rest.update = Mock( "rest.RestData.update", side_effect=self.update_side_effect( '{ "toplevel": {"master_value": "master", "second_level": {"some_json_key": "some_json_value", "some_json_key2": "some_json_value2" } } }', CaseInsensitiveDict({"Content-Type": "application/json"}), ), ) self.sensor = rest.RestSensor( self.hass, self.rest, self.name, self.unit_of_measurement, self.device_class, value_template, ["some_json_key", "some_json_key2"], self.force_update, self.resource_template, json_attrs_path, ) self.sensor.update() assert "some_json_value" == self.sensor.device_state_attributes[ "some_json_key"] assert ("some_json_value2" == self.sensor.device_state_attributes["some_json_key2"]) assert "master" == self.sensor.state
def __init__( self, friendly_name, area, price_type, precision, low_price_cutoff, currency, vat, use_cents, api, ad_template, hass, ) -> None: self._friendly_name = friendly_name or "%s %s %s" % ( DEFAULT_NAME, price_type, area, ) self._area = area self._currency = currency or _REGIONS[area][0] self._price_type = price_type self._precision = precision self._low_price_cutoff = low_price_cutoff self._use_cents = use_cents self._api = api self._ad_template = ad_template self._hass = hass if vat is True: self._vat = _REGIONS[area][2] else: self._vat = 0 # Price by current hour. self._current_price = None # Holds the data for today and morrow. self._data_today = None self._data_tomorrow = None # Values for the day self._average = None self._max = None self._min = None self._off_peak_1 = None self._off_peak_2 = None self._peak = None # Check incase the sensor was setup using config flow. # This blow up if the template isnt valid. if not isinstance(self._ad_template, Template): self._ad_template = cv.template(self._ad_template) attach(self._hass, self._ad_template) # To control the updates. self._last_tick = None self._cbs = []
def get_camera_component(self, camera): entity = None try: camera_id = camera.get("optionValue") camera_name = camera.get("optionDisplay") device_name = f"{camera_name} ({camera_id})" entity_name = f"{DEFAULT_NAME} {camera_name}" username = self._api.username password = self._api.password base_url = self._api.base_url session_id = self._api.session_id unique_id = f"{DOMAIN}-{DOMAIN_CAMERA}-{entity_name}" still_image_url = f'{base_url}/image/{camera_id}?q=100&s=100&session={session_id}' still_image_url_template = cv.template(still_image_url) stream_source = f'{base_url}/h264/{camera_id}/temp.m3u8&session={session_id}' camera_details = { CONF_NAME: f"{DEFAULT_NAME} {camera_name}", CONF_STILL_IMAGE_URL: still_image_url_template, CONF_STREAM_SOURCE: stream_source, CONF_LIMIT_REFETCH_TO_URL_CHANGE: False, CONF_FRAMERATE: 2, CONF_CONTENT_TYPE: DEFAULT_CONTENT_TYPE, CONF_VERIFY_SSL: False, CONF_USERNAME: username, CONF_PASSWORD: password, CONF_AUTHENTICATION: AUTHENTICATION_BASIC } attributes = { ATTR_FRIENDLY_NAME: entity_name, CONF_STREAM_SOURCE: stream_source, CONF_STILL_IMAGE_URL: still_image_url } for key in ATTR_BLUE_IRIS_CAMERA: if key in camera and key not in [CONF_NAME, CONF_ID]: key_name = ATTR_BLUE_IRIS_CAMERA[key] attributes[key_name] = camera[key] entity = { ENTITY_ID: camera_id, ENTITY_UNIQUE_ID: unique_id, ENTITY_NAME: entity_name, ENTITY_ATTRIBUTES: attributes, ENTITY_ICON: DEFAULT_ICON, ENTITY_DEVICE_NAME: device_name, ENTITY_CAMERA_DETAILS: camera_details } except Exception as ex: _LOGGER.error( f'Failed to get camera for {camera}, Error: {str(ex)}') return entity
async def async_step_config(self, user_input=None): """Initial configuration options are provided by the user.""" errors = {} if user_input is not None: self._namespace = user_input[CONF_NAMESPACE] self._allow_events = user_input[CONF_ALLOW_EVENTS] try: cv.template(str(user_input[CONF_NAME_TEMPLATE])) self._name_template = user_input[CONF_NAME_TEMPLATE] return await self.async_step_interfaces() except (vol.Invalid, TemplateError): errors["base"] = "template_failed" return self.async_show_form( step_id="config", data_schema=CONFIG_STEP_SCHEMA, errors=errors )
def __init__( self, hass: HomeAssistant, device_info: Mapping[str, Any], identifier: str, title: str, ) -> None: """Initialize a generic camera.""" super().__init__() self.hass = hass self._attr_unique_id = identifier self._authentication = device_info.get(CONF_AUTHENTICATION) self._username = device_info.get(CONF_USERNAME) self._password = device_info.get(CONF_PASSWORD) self._name = device_info.get(CONF_NAME, title) self._still_image_url = device_info.get(CONF_STILL_IMAGE_URL) if ( not isinstance(self._still_image_url, template_helper.Template) and self._still_image_url ): self._still_image_url = cv.template(self._still_image_url) if self._still_image_url: self._still_image_url.hass = hass self._stream_source = device_info.get(CONF_STREAM_SOURCE) if self._stream_source: if not isinstance(self._stream_source, template_helper.Template): self._stream_source = cv.template(self._stream_source) self._stream_source.hass = hass self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE] self._attr_frame_interval = 1 / device_info[CONF_FRAMERATE] self._attr_supported_features = ( CameraEntityFeature.STREAM if self._stream_source else 0 ) self.content_type = device_info[CONF_CONTENT_TYPE] self.verify_ssl = device_info[CONF_VERIFY_SSL] if device_info.get(CONF_RTSP_TRANSPORT): self.stream_options[CONF_RTSP_TRANSPORT] = device_info[CONF_RTSP_TRANSPORT] self._auth = generate_auth(device_info) if device_info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): self.stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True self._last_url = None self._last_image = None
def setUp(self): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() self.initial_state = 'initial_state' self.rest = Mock('rest.RestData') self.rest.update = Mock('rest.RestData.update', side_effect=self.update_side_effect( '{ "key": "' + self.initial_state + '" }')) self.name = 'foo' self.unit_of_measurement = 'MB' self.value_template = template('{{ value_json.key }}') self.value_template.hass = self.hass self.sensor = rest.RestSensor(self.hass, self.rest, self.name, self.unit_of_measurement, self.value_template)
def setUp(self): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() self.initial_state = 'initial_state' self.rest = Mock('rest.RestData') self.rest.update = Mock('rest.RestData.update', side_effect=self.update_side_effect( '{ "key": "' + self.initial_state + '" }')) self.name = 'foo' self.unit_of_measurement = 'MB' self.value_template = template('{{ value_json.key }}') self.value_template.hass = self.hass self.sensor = rest.RestSensor(self.hass, self.rest, self.name, self.unit_of_measurement, self.value_template)
def get_miot_props(self, device, mapping: dict = None): if mapping is None: mapping = device.mapping or {} rls = [] if dic := self.get_miio_props(device): for k, v in mapping.items(): u = MiotSpec.unique_prop(v, valid=True) c = self.specs.get(u, {}) p = c.get('prop') if not p: continue val = dic.get(p) prop = self.miot_spec.specs.get(u) if prop and isinstance(prop, MiotProperty): mph = MiioPropertyHelper(prop) fmt = c.get('format') try: if tpl := c.get('template', {}): tpl = CUSTOM_TEMPLATES.get(tpl, tpl) tpl = cv.template(tpl) tpl.hass = self.hass val = tpl.render({ 'value': val, 'props': dic, 'dict': c.get('dict', {}), 'min': prop.range_min(), 'max': prop.range_max(), 'step': prop.range_step(), 'description': prop.list_description(val) if prop.value_list else None, }) elif fmt and hasattr(mph, fmt): val = getattr(mph, fmt)(val) elif d := c.get('dict', {}): val = d.get(val, c.get('default', val)) elif prop.format in ['bool']: val = cv.boolean(val)
def call_action(self, device, siid, aiid, params): key = MiotSpec.unique_prop(siid=siid, aiid=aiid) cfg = self.specs.get(key, {}) setter = cfg.get('setter') pms = cv.ensure_list(params) act = self.miot_spec.specs.get(key) if act and isinstance(act, MiotAction): if tpl := cfg.get('set_template'): tpl = CUSTOM_TEMPLATES.get(tpl, tpl) tpl = cv.template(tpl) tpl.hass = self.hass pms = tpl.render({ 'params': pms, 'props': self.miio_props_values, }) or [] if isinstance(pms, dict) and 'method' in pms: setter = pms.get('method', setter) pms = pms.get('params', [])
def get_miio_props(self, device): dic = {} if not self.config.get('without_props'): try: num = int(self.config.get('chunk_properties')) except (TypeError, ValueError): num = None try: vls = device.get_properties(self.miio_props, max_properties=num) dic.update(dict(zip(self.miio_props, vls))) except (DeviceException, OSError) as exc: if is_offline_exception(exc): raise exc _LOGGER.error( '%s: Got MiioException: %s while get_properties(%s)', self.model, exc, self.miio_props) if cls := self.config.get('miio_commands'): for c in cls: if dly := c.get('delay', 0): time.sleep(dly) pms = c.get('params', []) try: vls = device.send(c['method'], pms) except (DeviceException, OSError) as exc: if is_offline_exception(exc): raise exc _LOGGER.error('%s: Got MiioException: %s while %s(%s)', self.model, exc, c['method'], pms) continue kls = c.get('values', []) if kls is True: kls = c.get('params', []) if tpl := c.get('template'): tpl = CUSTOM_TEMPLATES.get(tpl, tpl) tpl = cv.template(tpl) tpl.hass = self.hass pdt = tpl.render({'results': vls}) if isinstance(pdt, dict): dic.update(pdt)
def get_miio_props(self, device): dic = {} if not self.config.get('without_props'): try: num = int(self.config.get('chunk_properties')) except (TypeError, ValueError): num = None vls = device.get_properties(self.miio_props, max_properties=num) dic.update(dict(zip(self.miio_props, vls))) if cls := self.config.get('miio_commands'): for c in cls: if dly := c.get('delay', 0): time.sleep(dly) vls = device.send(c['method'], c.get('params', [])) kls = c.get('values', []) if kls is True: kls = c.get('params', []) if tpl := c.get('template'): tpl = CUSTOM_TEMPLATES.get(tpl, tpl) tpl = cv.template(tpl) tpl.hass = self.hass pdt = tpl.render({'results': vls}) if isinstance(pdt, dict): dic.update(pdt)
def __init__( self, hass, device_id, friendly_name, start_time, run_freq, run_days, irrigation_on, icon, wait_icon, rain_icon, zones, unique_id, ): self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass ) """Initialize a Irrigation program.""" self._name = str(friendly_name).title() self._program_name = str(friendly_name).title() self._start_time = start_time self._run_freq = run_freq self._run_days = run_days self._irrigation_on = irrigation_on self._icon = icon self._wait_icon = wait_icon self._rain_icon = rain_icon self._zones = zones self._state_attributes = None self._state = False self._unique_id = unique_id self._stop = False self._device_id = device_id self._running = False self._last_run = None self._triggered_manually = True self._template = None """ Validate and Build a template from the attributes provided """ _LOGGER.debug('Start Time %s: %s',self._start_time, hass.states.get(self._start_time)) template = "states('sensor.time')" + " + ':00' == states('" + self._start_time + "') " if self._irrigation_on is not None: _LOGGER.debug('Irrigation on %s: %s',self._irrigation_on, hass.states.get(self._irrigation_on)) template = template + " and is_state('" + self._irrigation_on + "', 'on') " if self._run_days is not None: _LOGGER.debug('Run Days %s: %s',self._run_days, hass.states.get(self._run_days)) template = template + " and now().strftime('%a') in states('" + self._run_days + "')" if self._run_freq is not None: _LOGGER.debug('Run Frequency %s: %s',self._run_freq, hass.states.get(self._run_freq)) template = template + \ " and states('" + run_freq + "')|int" + \ " <= ((as_timestamp(now()) " + \ "- as_timestamp(states." + self.entity_id + \ ".attributes.last_ran) | int) /86400) | int(0) " template = "{{ " + template + " }}" _LOGGER.debug('-------------------- on start: %s ----------------------------',self._name) _LOGGER.debug('Template: %s', template) template = cv.template(template) template.hass = hass self._template = template
kls = c.get('values', []) if kls is True: kls = c.get('params', []) if tpl := c.get('template'): tpl = CUSTOM_TEMPLATES.get(tpl, tpl) tpl = cv.template(tpl) tpl.hass = self.hass pdt = tpl.render({'results': vls}) if isinstance(pdt, dict): dic.update(pdt) elif kls: if len(kls) == len(vls): dic.update(dict(zip(kls, vls))) if tpl := self.config.get('miio_template'): tpl = CUSTOM_TEMPLATES.get(tpl, tpl) tpl = cv.template(tpl) tpl.hass = self.hass pdt = tpl.render({'props': dic}) if isinstance(pdt, dict): dic.update(pdt) self.miio_props_values = dic _LOGGER.info('%s: Got miio props for miot: %s', self.model, dic) return dic async def async_get_miot_props(self, *args, **kwargs): return await self.hass.async_add_executor_job( partial(self.get_miot_props, *args, **kwargs)) def get_miot_props(self, device, mapping: dict = None): if mapping is None: mapping = device.mapping or {}
def __init__( self, friendly_name, area, price_type, precision, low_price_cutoff, currency, vat, use_cents, api, ad_template, hass, ) -> None: # friendly_name is ignored as it never worked. # rename the sensor in the ui if you dont like the name. self._area = area self._currency = currency or _REGIONS[area][0] self._price_type = price_type self._precision = precision self._low_price_cutoff = low_price_cutoff self._use_cents = use_cents self._api = api self._ad_template = ad_template self._hass = hass if vat is True: self._vat = _REGIONS[area][2] else: self._vat = 0 # Price by current hour. self._current_price = None # Holds the data for today and morrow. self._data_today = None self._data_tomorrow = None # Values for the day self._average = None self._max = None self._min = None self._off_peak_1 = None self._off_peak_2 = None self._peak = None # Check incase the sensor was setup using config flow. # This blow up if the template isnt valid. if not isinstance(self._ad_template, Template): if self._ad_template in (None, ""): self._ad_template = DEFAULT_TEMPLATE self._ad_template = cv.template(self._ad_template) # check for yaml setup. else: if self._ad_template.template in ("", None): self._ad_template = cv.template(DEFAULT_TEMPLATE) attach(self._hass, self._ad_template) # To control the updates. self._last_tick = None self._cbs = []
def get_camera_component(self, camera: CameraData) -> EntityData: entity = None try: device_name = self.device_manager.get_camera_device_name(camera) entity_name = f"{self.integration_title} {camera.name}" username = self.config_data.username password = self.config_data.password_clear_text base_url = self.api.base_url session_id = self.api.session_id unique_id = f"{DOMAIN}-{DOMAIN_CAMERA}-{entity_name}" still_image_url = f"{base_url}/image/{camera.id}?session={session_id}" still_image_url_template = cv.template(still_image_url) stream_config = STREAM_VIDEO.get(self.config_data.stream_type, {}) file_name = stream_config.get("file_name", "") stream_name = stream_config.get("stream_name") support_stream = False if DOMAIN_STREAM in self.hass.data: support_stream = self.config_data.support_stream stream_source = ( f"{base_url}/{stream_name}/{camera.id}/{file_name}?session={session_id}" ) fps = camera.data.get("FPS", 1) if fps < 1: fps = 1 camera_details = { CONF_NAME: f"{entity_name}", CONF_STILL_IMAGE_URL: still_image_url_template, CONF_STREAM_SOURCE: stream_source, CONF_LIMIT_REFETCH_TO_URL_CHANGE: False, CONF_FRAMERATE: fps, CONF_CONTENT_TYPE: DEFAULT_CONTENT_TYPE, CONF_VERIFY_SSL: False, CONF_USERNAME: username, CONF_PASSWORD: password, CONF_AUTHENTICATION: AUTHENTICATION_BASIC, CONF_SUPPORT_STREAM: support_stream, } attributes = { ATTR_FRIENDLY_NAME: entity_name, CONF_STREAM_SOURCE: stream_source, CONF_STILL_IMAGE_URL: still_image_url, } for key in ATTR_BLUE_IRIS_CAMERA: key_name = ATTR_BLUE_IRIS_CAMERA[key] attributes[key_name] = camera.data.get(key, "N/A") entity = EntityData() entity.id = camera.id entity.unique_id = unique_id entity.name = entity_name entity.attributes = attributes entity.icon = DEFAULT_ICON entity.device_name = device_name entity.details = camera_details entity.state = camera.is_online except Exception as ex: self.log_exception(ex, f"Failed to get camera for {camera}") return entity
def get_camera_component(self, camera: CameraData) -> EntityData: entity = None try: device_name = self.device_manager.get_camera_device_name(camera) entity_name = f"{self.integration_title} {camera.name}" username = self.config_data.username password = self.config_data.password_clear_text base_url = self.api.base_url unique_id = f"{DOMAIN}-{DOMAIN_CAMERA}-{entity_name}" snapshot = f"{base_url}{camera.snapshot}" still_image_url_template = cv.template(snapshot) support_stream = DOMAIN_STREAM in self.hass.data stream_source = "" for stream in camera.streams: stream_source = f"{base_url}{stream}" break camera_details = { CONF_NAME: f"{entity_name}", CONF_STILL_IMAGE_URL: still_image_url_template, CONF_STREAM_SOURCE: stream_source, CONF_LIMIT_REFETCH_TO_URL_CHANGE: False, CONF_FRAMERATE: camera.fps, CONF_CONTENT_TYPE: DEFAULT_CONTENT_TYPE, CONF_VERIFY_SSL: False, CONF_USERNAME: username, CONF_PASSWORD: password, CONF_AUTHENTICATION: AUTHENTICATION_BASIC, CONF_SUPPORT_STREAM: support_stream, } attributes = { ATTR_FRIENDLY_NAME: entity_name, CONF_STREAM_SOURCE: stream_source, CONF_STILL_IMAGE_URL: snapshot } for key in CAMERA_ATTRIBUTES: key_name = CAMERA_ATTRIBUTES[key] attributes[key_name] = camera.details.get(key, "N/A") monitor_details = camera.details.get("details", {}) for key in CAMERA_DETAILS_ATTRIBUTES: key_name = CAMERA_DETAILS_ATTRIBUTES[key] attributes[key_name] = monitor_details.get(key, "N/A") entity = EntityData() entity.id = camera.monitorId entity.unique_id = unique_id entity.name = entity_name entity.attributes = attributes entity.icon = DEFAULT_ICON entity.device_name = device_name entity.details = camera_details entity.state = camera.status except Exception as ex: self.log_exception(ex, f"Failed to get camera for {camera}") return entity
def get_camera_component(self, camera) -> EntityData: entity = None try: camera_id = camera.get("optionValue") camera_name = camera.get("optionDisplay") is_online = camera.get("isOnline") device_name = f"{camera_name} ({camera_id})" entity_name = f"{DEFAULT_NAME} {camera_name}" username = self.config_data.username password = self.config_data.password_clear_text base_url = self.api.base_url session_id = self.api.session_id unique_id = f"{DOMAIN}-{DOMAIN_CAMERA}-{entity_name}" still_image_url = f'{base_url}/image/{camera_id}?q=100&s=100&session={session_id}' still_image_url_template = cv.template(still_image_url) stream_source = f'{base_url}/h264/{camera_id}/temp.m3u8&session={session_id}' camera_details = { CONF_NAME: f"{DEFAULT_NAME} {camera_name}", CONF_STILL_IMAGE_URL: still_image_url_template, CONF_STREAM_SOURCE: stream_source, CONF_LIMIT_REFETCH_TO_URL_CHANGE: False, CONF_FRAMERATE: 2, CONF_CONTENT_TYPE: DEFAULT_CONTENT_TYPE, CONF_VERIFY_SSL: False, CONF_USERNAME: username, CONF_PASSWORD: password, CONF_AUTHENTICATION: AUTHENTICATION_BASIC } attributes = { ATTR_FRIENDLY_NAME: entity_name, CONF_STREAM_SOURCE: stream_source, CONF_STILL_IMAGE_URL: still_image_url } for key in ATTR_BLUE_IRIS_CAMERA: if key in camera and key not in [CONF_NAME, CONF_ID]: key_name = ATTR_BLUE_IRIS_CAMERA[key] attributes[key_name] = camera[key] entity = EntityData() entity.id = camera_id entity.unique_id = unique_id entity.name = entity_name entity.attributes = attributes entity.icon = DEFAULT_ICON entity.device_name = device_name entity.details = camera_details entity.state = is_online except Exception as ex: self.log_exception(ex, f'Failed to get camera for {camera}') return entity
if fmt is None: # if PIL can't figure it out, could be svg. with contextlib.suppress(UnicodeDecodeError): if image.decode("utf-8").lstrip().startswith("<svg"): return "svg+xml" return fmt async def async_test_still(hass, info) -> tuple[dict[str, str], str | None]: """Verify that the still image is valid before we create an entity.""" fmt = None if not (url := info.get(CONF_STILL_IMAGE_URL)): return {}, info.get(CONF_CONTENT_TYPE, "image/jpeg") if not isinstance(url, template_helper.Template) and url: url = cv.template(url) url.hass = hass try: url = url.async_render(parse_result=False) except TemplateError as err: _LOGGER.warning("Problem rendering template %s: %s", url, err) return {CONF_STILL_IMAGE_URL: "template_error"}, None verify_ssl = info.get(CONF_VERIFY_SSL) auth = generate_auth(info) try: async_client = get_async_client(hass, verify_ssl=verify_ssl) async with timeout(GET_IMAGE_TIMEOUT): response = await async_client.get(url, auth=auth, timeout=GET_IMAGE_TIMEOUT) response.raise_for_status()