def test_load_yaml_config_raises_error_if_malformed_yaml(self): """Test error raised if invalid YAML.""" with open(YAML_PATH, 'w') as f: f.write(':') with pytest.raises(HomeAssistantError): config_util.load_yaml_config_file(YAML_PATH)
def test_load_yaml_config_raises_error_if_not_dict(self): """Test error raised when YAML file is not a dict.""" with open(YAML_PATH, 'w') as f: f.write('5') with pytest.raises(HomeAssistantError): config_util.load_yaml_config_file(YAML_PATH)
def test_load_yaml_config_raises_error_if_not_dict(self): """ Test error raised when YAML file is not a dict. """ with open(YAML_PATH, "w") as f: f.write("5") with self.assertRaises(HomeAssistantError): config_util.load_yaml_config_file(YAML_PATH)
def test_load_yaml_config_raises_error_if_unsafe_yaml(self): """Test error raised if unsafe YAML.""" with open(YAML_PATH, 'w') as f: f.write('hello: !!python/object/apply:os.system') with pytest.raises(HomeAssistantError): config_util.load_yaml_config_file(YAML_PATH)
def test_load_yaml_config_raises_error_if_malformed_yaml(self): """ Test error raised if invalid YAML. """ with open(YAML_PATH, "w") as f: f.write(":") with self.assertRaises(HomeAssistantError): config_util.load_yaml_config_file(YAML_PATH)
def setup(hass, config): """Set up the Verisure component.""" import verisure global HUB HUB = VerisureHub(config[DOMAIN], verisure) if not HUB.login(): return False hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, lambda event: HUB.logout()) HUB.update_overview() for component in ('sensor', 'switch', 'alarm_control_panel', 'lock', 'camera', 'binary_sensor'): discovery.load_platform(hass, component, DOMAIN, {}, config) descriptions = conf_util.load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def capture_smartcam(service): """Capture a new picture from a smartcam.""" device_id = service.data.get(ATTR_DEVICE_SERIAL) HUB.smartcam_capture(device_id) _LOGGER.debug("Capturing new image from %s", ATTR_DEVICE_SERIAL) hass.services.register(DOMAIN, SERVICE_CAPTURE_SMARTCAM, capture_smartcam, descriptions[DOMAIN][SERVICE_CAPTURE_SMARTCAM], schema=CAPTURE_IMAGE_SCHEMA) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Dyson fan components.""" _LOGGER.info("Creating new Dyson fans") if DYSON_FAN_DEVICES not in hass.data: hass.data[DYSON_FAN_DEVICES] = [] # Get Dyson Devices from parent component for device in hass.data[DYSON_DEVICES]: dyson_entity = DysonPureCoolLinkDevice(hass, device) hass.data[DYSON_FAN_DEVICES].append(dyson_entity) add_devices(hass.data[DYSON_FAN_DEVICES]) descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) def service_handle(service): """Handle dyson services.""" entity_id = service.data.get('entity_id') night_mode = service.data.get('night_mode') fan_device = next([fan for fan in hass.data[DYSON_FAN_DEVICES] if fan.entity_id == entity_id].__iter__(), None) if fan_device is None: _LOGGER.warning("Unable to find Dyson fan device %s", str(entity_id)) return if service.service == SERVICE_SET_NIGHT_MODE: fan_device.night_mode(night_mode) # Register dyson service(s) hass.services.register(DOMAIN, SERVICE_SET_NIGHT_MODE, service_handle, descriptions.get(SERVICE_SET_NIGHT_MODE), schema=DYSON_SET_NIGHT_MODE_SCHEMA)
def test_scan_devices(self): """Test creating device info (MAC, name) from response. The created known_devices.yaml device info is compared to the DD-WRT Lan Status request response fixture. This effectively checks the data parsing functions. """ with requests_mock.Mocker() as mock_request: mock_request.register_uri( 'GET', r'http://%s/Status_Wireless.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Wireless.txt')) mock_request.register_uri( 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Lan.txt')) with assert_setup_component(1): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', CONF_HOST: TEST_HOST, CONF_USERNAME: '******', CONF_PASSWORD: '******' }}) self.hass.block_till_done() path = self.hass.config.path(device_tracker.YAML_DEVICES) devices = config.load_yaml_config_file(path) for device in devices: self.assertIn( devices[device]['mac'], load_fixture('Ddwrt_Status_Lan.txt')) self.assertIn( slugify(devices[device]['name']), load_fixture('Ddwrt_Status_Lan.txt'))
def autosetup_ihc_products(hass: HomeAssistantType, config, ihc_controller): """Auto setup of IHC products from the ihc project file.""" project_xml = ihc_controller.get_project() if not project_xml: _LOGGER.error("Unable to read project from ihc controller.") return False project = xml.etree.ElementTree.fromstring(project_xml) # if an auto setup file exist in the configuration it will override yaml_path = hass.config.path(AUTO_SETUP_YAML) if not os.path.isfile(yaml_path): yaml_path = os.path.join(os.path.dirname(__file__), AUTO_SETUP_YAML) yaml = load_yaml_config_file(yaml_path) try: auto_setup_conf = AUTO_SETUP_SCHEMA(yaml) except vol.Invalid as exception: _LOGGER.error("Invalid IHC auto setup data: %s", exception) return False groups = project.findall('.//group') for component in IHC_PLATFORMS: component_setup = auto_setup_conf[component] discovery_info = get_discovery_info(component_setup, groups) if discovery_info: discovery.load_platform(hass, component, DOMAIN, discovery_info, config) return True
def setup(hass, config): """Track states and offer events for sensors.""" component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS) component.setup(config) def alarm_service_handler(service): """Map services to methods on Alarm.""" target_alarms = component.extract_from_service(service) if ATTR_CODE not in service.data: code = None else: code = service.data[ATTR_CODE] method = SERVICE_TO_METHOD[service.service] for alarm in target_alarms: getattr(alarm, method)(code) if alarm.should_poll: alarm.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) for service in SERVICE_TO_METHOD: hass.services.register(DOMAIN, service, alarm_service_handler, descriptions.get(service)) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Bose Soundtouch platform.""" name = config.get(CONF_NAME) remote_config = { 'name': 'HomeAssistant', 'description': config.get(CONF_NAME), 'id': 'ha.component.soundtouch', 'port': config.get(CONF_PORT), 'host': config.get(CONF_HOST) } soundtouch_device = SoundTouchDevice(name, remote_config) DEVICES.append(soundtouch_device) add_devices([soundtouch_device]) descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_PLAY_EVERYWHERE, play_everywhere_service, descriptions.get(SERVICE_PLAY_EVERYWHERE), schema=SOUNDTOUCH_PLAY_EVERYWHERE) hass.services.register(DOMAIN, SERVICE_CREATE_ZONE, create_zone_service, descriptions.get(SERVICE_CREATE_ZONE), schema=SOUNDTOUCH_CREATE_ZONE_SCHEMA) hass.services.register(DOMAIN, SERVICE_REMOVE_ZONE_SLAVE, remove_zone_slave, descriptions.get(SERVICE_REMOVE_ZONE_SLAVE), schema=SOUNDTOUCH_REMOVE_ZONE_SCHEMA) hass.services.register(DOMAIN, SERVICE_ADD_ZONE_SLAVE, add_zone_slave, descriptions.get(SERVICE_ADD_ZONE_SLAVE), schema=SOUNDTOUCH_ADD_ZONE_SCHEMA)
def get_service(hass, config): """Return push service.""" descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) name = config.get("name") if name is None: logging.error("Name must be specified.") return None cert_file = config.get('cert_file') if cert_file is None: logging.error("Certificate must be specified.") return None topic = config.get('topic') if topic is None: logging.error("Topic must be specified.") return None sandbox = bool(config.get('sandbox', False)) service = ApnsNotificationService(hass, name, topic, sandbox, cert_file) hass.services.register(DOMAIN, name, service.register, descriptions.get(SERVICE_REGISTER), schema=REGISTER_SERVICE_SCHEMA) return service
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Nuki lock platform.""" from pynuki import NukiBridge bridge = NukiBridge(config.get(CONF_HOST), config.get(CONF_TOKEN)) add_devices([NukiLock(lock) for lock in bridge.locks]) def service_handler(service): """Service handler for nuki services.""" entity_ids = extract_entity_ids(hass, service) all_locks = hass.data[NUKI_DATA][DOMAIN] target_locks = [] if not entity_ids: target_locks = all_locks else: for lock in all_locks: if lock.entity_id in entity_ids: target_locks.append(lock) for lock in target_locks: if service.service == SERVICE_LOCK_N_GO: unlatch = service.data[ATTR_UNLATCH] lock.lock_n_go(unlatch=unlatch) elif service.service == SERVICE_UNLATCH: lock.unlatch() descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register( DOMAIN, SERVICE_LOCK_N_GO, service_handler, descriptions.get(SERVICE_LOCK_N_GO), schema=LOCK_N_GO_SERVICE_SCHEMA) hass.services.register( DOMAIN, SERVICE_UNLATCH, service_handler, descriptions.get(SERVICE_UNLATCH), schema=UNLATCH_SERVICE_SCHEMA)
def register_services(hass): """Register all services for sonos devices.""" descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_GROUP_PLAYERS, _group_players_service, descriptions.get(SERVICE_GROUP_PLAYERS), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_UNJOIN, _unjoin_service, descriptions.get(SERVICE_UNJOIN), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_SNAPSHOT, _snapshot_service, descriptions.get(SERVICE_SNAPSHOT), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_RESTORE, _restore_service, descriptions.get(SERVICE_RESTORE), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_TIMER, _set_sleep_timer_service, descriptions.get(SERVICE_SET_TIMER), schema=SONOS_SET_TIMER_SCHEMA) hass.services.register(DOMAIN, SERVICE_CLEAR_TIMER, _clear_sleep_timer_service, descriptions.get(SERVICE_CLEAR_TIMER), schema=SONOS_SCHEMA)
def from_config_file(config_path: str, hass: Optional[core.HomeAssistant]=None, verbose: bool=False, skip_pip: bool=True, log_rotate_days: Any=None): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter if given, instantiates a new Home Assistant object if 'hass' is not given. """ if hass is None: hass = core.HomeAssistant() # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir mount_local_lib_path(config_dir) enable_logging(hass, verbose, log_rotate_days) try: config_dict = conf_util.load_yaml_config_file(config_path) except HomeAssistantError: return None return from_config_dict(config_dict, hass, enable_log=False, skip_pip=skip_pip)
def setup(hass, config): """Setup the Foursquare component.""" descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) config = config[DOMAIN] def checkin_user(call): """Check a user in on Swarm.""" url = ("https://api.foursquare.com/v2/checkins/add" "?oauth_token={}" "&v=20160802" "&m=swarm").format(config[CONF_ACCESS_TOKEN]) response = requests.post(url, data=call.data, timeout=10) if response.status_code not in (200, 201): _LOGGER.exception( "Error checking in user. Response %d: %s:", response.status_code, response.reason) hass.bus.fire(EVENT_CHECKIN, response.text) # Register our service with Home Assistant. hass.services.register(DOMAIN, 'checkin', checkin_user, descriptions[DOMAIN][SERVICE_CHECKIN], schema=CHECKIN_SERVICE_SCHEMA) hass.wsgi.register_view(FoursquarePushReceiver( hass, config[CONF_PUSH_SECRET])) return True
def setup(hass, config): """Set up the media extractor service.""" descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'media_player', 'services.yaml')) def play_media(call): """Get stream URL and send it to the media_player.play_media.""" media_url = call.data.get(ATTR_MEDIA_CONTENT_ID) try: stream_url = get_media_stream_url(media_url) except YDException: _LOGGER.error("Could not retrieve data for the URL: %s", media_url) return else: data = {k: v for k, v in call.data.items() if k != ATTR_MEDIA_CONTENT_ID} data[ATTR_MEDIA_CONTENT_ID] = stream_url hass.async_add_job( hass.services.async_call( MEDIA_PLAYER_DOMAIN, SERVICE_PLAY_MEDIA, data) ) hass.services.register(DOMAIN, SERVICE_PLAY_MEDIA, play_media, description=descriptions[SERVICE_PLAY_MEDIA], schema=MEDIA_PLAYER_PLAY_MEDIA_SCHEMA) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Wink platform.""" import pywink for lock in pywink.get_locks(): _id = lock.object_id() + lock.name() if _id not in hass.data[DOMAIN]['unique_ids']: add_devices([WinkLockDevice(lock, hass)]) def service_handle(service): """Handler for services.""" entity_ids = service.data.get('entity_id') all_locks = hass.data[DOMAIN]['entities']['lock'] locks_to_set = [] if entity_ids is None: locks_to_set = all_locks else: for lock in all_locks: if lock.entity_id in entity_ids: locks_to_set.append(lock) for lock in locks_to_set: if service.service == SERVICE_SET_VACATION_MODE: lock.set_vacation_mode(service.data.get(ATTR_ENABLED)) elif service.service == SERVICE_SET_ALARM_STATE: lock.set_alarm_state(service.data.get(ATTR_ENABLED)) elif service.service == SERVICE_SET_BEEPER_STATE: lock.set_beeper_state(service.data.get(ATTR_ENABLED)) elif service.service == SERVICE_SET_ALARM_MODE: lock.set_alarm_mode(service.data.get(ATTR_MODE)) elif service.service == SERVICE_SET_ALARM_SENSITIVITY: lock.set_alarm_sensitivity(service.data.get(ATTR_SENSITIVITY)) descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_SET_VACATION_MODE, service_handle, descriptions.get(SERVICE_SET_VACATION_MODE), schema=SET_ENABLED_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_ALARM_STATE, service_handle, descriptions.get(SERVICE_SET_ALARM_STATE), schema=SET_ENABLED_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_BEEPER_STATE, service_handle, descriptions.get(SERVICE_SET_BEEPER_STATE), schema=SET_ENABLED_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_ALARM_MODE, service_handle, descriptions.get(SERVICE_SET_ALARM_MODE), schema=SET_ALARM_MODES_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_ALARM_SENSITIVITY, service_handle, descriptions.get(SERVICE_SET_ALARM_SENSITIVITY), schema=SET_SENSITIVITY_SCHEMA)
def setup_platform(hass, config, add_devices, discovery_info=None): """Perform the setup for Envisalink alarm panels.""" _configured_partitions = discovery_info['partitions'] _code = discovery_info[CONF_CODE] _panic_type = discovery_info[CONF_PANIC] for part_num in _configured_partitions: _device_config_data = PARTITION_SCHEMA( _configured_partitions[part_num]) _device = EnvisalinkAlarm( part_num, _device_config_data[CONF_PARTITIONNAME], _code, _panic_type, EVL_CONTROLLER.alarm_state['partition'][part_num], EVL_CONTROLLER) DEVICES.append(_device) add_devices(DEVICES) # Register Envisalink specific services descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register(alarm.DOMAIN, SERVICE_ALARM_KEYPRESS, alarm_keypress_handler, descriptions.get(SERVICE_ALARM_KEYPRESS), schema=ALARM_KEYPRESS_SCHEMA) return True
def register_services(hass): """Register all services for harmony devices.""" descriptions = load_yaml_config_file(path.join(path.dirname(__file__), "services.yaml")) hass.services.register( DOMAIN, SERVICE_SYNC, _sync_service, descriptions.get(SERVICE_SYNC), schema=HARMONY_SYNC_SCHEMA )
def load_config(path: str, hass: HomeAssistantType, consider_home: timedelta): """Load devices from YAML configuration file.""" dev_schema = vol.Schema({ vol.Required('name'): cv.string, vol.Optional('track', default=False): cv.boolean, vol.Optional('mac', default=None): vol.Any(None, vol.All(cv.string, vol.Upper)), vol.Optional(CONF_AWAY_HIDE, default=DEFAULT_AWAY_HIDE): cv.boolean, vol.Optional('gravatar', default=None): vol.Any(None, cv.string), vol.Optional('picture', default=None): vol.Any(None, cv.string), vol.Optional(CONF_CONSIDER_HOME, default=consider_home): vol.All( cv.time_period, cv.positive_timedelta) }) try: result = [] devices = load_yaml_config_file(path) for dev_id, device in devices.items(): try: device = dev_schema(device) device['dev_id'] = cv.slugify(dev_id) except vol.Invalid as exp: log_exception(exp, dev_id, devices) else: result.append(Device(hass, **device)) return result except (HomeAssistantError, FileNotFoundError): # When YAML file could not be loaded/did not contain a dict return []
def setup(hass, config): """ Track states and offer events for remote. """ component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS) component.setup(config) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def remote_service_handler(service): """ Maps services to methods on RemoteDevice. """ target_remotes = component.extract_from_service(service) method = SERVICE_TO_METHOD[service.service] for remote in target_remotes: getattr(remote, method)() if remote.should_poll: remote.update_ha_state(True) for service in SERVICE_TO_METHOD: hass.services.register(DOMAIN, service, remote_service_handler, descriptions.get(service)) return True
def setup(hass, config): """Track states and offer events for locks.""" component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, GROUP_NAME_ALL_LOCKS) component.setup(config) def handle_lock_service(service): """Handle calls to the lock services.""" target_locks = component.extract_from_service(service) if ATTR_CODE not in service.data: code = None else: code = service.data[ATTR_CODE] for item in target_locks: if service.service == SERVICE_LOCK: item.lock(code=code) else: item.unlock(code=code) if item.should_poll: item.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service, descriptions.get(SERVICE_UNLOCK)) hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service, descriptions.get(SERVICE_LOCK)) return True
def test_update_existing_device_with_tracking_id(self): """Test updating an existing device that has a tracking id.""" devices_path = self.hass.config.path("test_app_apns.yaml") with open(devices_path, "w+") as out: out.write("1234: {name: test device 1, " "tracking_device_id: tracking123}\n") out.write("5678: {name: test device 2, " "tracking_device_id: tracking456}\n") self._setup_notify() self.assertTrue( self.hass.services.call( notify.DOMAIN, "apns_test_app", {"push_id": "1234", "name": "updated device 1"}, blocking=True ) ) devices = {str(key): value for (key, value) in load_yaml_config_file(devices_path).items()} test_device_1 = devices.get("1234") test_device_2 = devices.get("5678") self.assertIsNotNone(test_device_1) self.assertIsNotNone(test_device_2) self.assertEqual("tracking123", test_device_1.get("tracking_device_id")) self.assertEqual("tracking456", test_device_2.get("tracking_device_id")) os.remove(devices_path)
def setup(hass, config): """ Track states and offer events for garage door. """ component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, GROUP_NAME_ALL_GARAGE_DOORS) component.setup(config) def handle_garage_door_service(service): """ Handles calls to the garage door services. """ target_locks = component.extract_from_service(service) for item in target_locks: if service.service == SERVICE_CLOSE: item.close_door() else: item.open_door() if item.should_poll: item.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service, descriptions.get(SERVICE_OPEN)) hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service, descriptions.get(SERVICE_CLOSE)) return True
def setup(hass, config): """Track states and offer events for covers.""" component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_COVERS) component.setup(config) def handle_cover_service(service): """Handle calls to the cover services.""" method = SERVICE_TO_METHOD.get(service.service) params = service.data.copy() params.pop(ATTR_ENTITY_ID, None) if method: for cover in component.extract_from_service(service): getattr(cover, method['method'])(**params) if cover.should_poll: cover.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) for service_name in SERVICE_TO_METHOD: schema = SERVICE_TO_METHOD[service_name].get( 'schema', COVER_SERVICE_SCHEMA) hass.services.register(DOMAIN, service_name, handle_cover_service, descriptions.get(service_name), schema=schema) return True
def test_register_new_device(self): """Test registering a new device with a name.""" config = { 'notify': { 'platform': 'apns', 'name': 'test_app', 'topic': 'testapp.appname', 'cert_file': 'test_app.pem' } } hass = get_test_home_assistant() devices_path = hass.config.path('test_app_apns.yaml') with open(devices_path, 'w+') as out: out.write('5678: {name: test device 2}\n') notify.setup(hass, config) self.assertTrue(hass.services.call('apns', 'test_app', {'push_id': '1234', 'name': 'test device'}, blocking=True)) devices = {str(key): value for (key, value) in load_yaml_config_file(devices_path).items()} test_device_1 = devices.get('1234') test_device_2 = devices.get('5678') self.assertIsNotNone(test_device_1) self.assertIsNotNone(test_device_2) self.assertEqual('test device', test_device_1.get('name')) os.remove(devices_path)
def test_device_name_no_dhcp(self): """Test creating device info (MAC) when missing dhcp response.""" with requests_mock.Mocker() as mock_request: mock_request.register_uri( 'GET', r'http://%s/Status_Wireless.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Wireless.txt')) mock_request.register_uri( 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Lan.txt'). replace('dhcp_leases', 'missing')) with assert_setup_component(1): assert setup_component( self.hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: 'ddwrt', CONF_HOST: TEST_HOST, CONF_USERNAME: '******', CONF_PASSWORD: '******' }}) self.hass.block_till_done() path = self.hass.config.path(device_tracker.YAML_DEVICES) devices = config.load_yaml_config_file(path) for device in devices: _LOGGER.error(devices[device]) self.assertIn( devices[device]['mac'], load_fixture('Ddwrt_Status_Lan.txt'))
def setup(hass, config): """Track states and offer events for switches.""" component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES) component.setup(config) def handle_switch_service(service): """Handle calls to the switch services.""" target_switches = component.extract_from_service(service) for switch in target_switches: if service.service == SERVICE_TURN_ON: switch.turn_on() elif service.service == SERVICE_TOGGLE: switch.toggle() else: switch.turn_off() if switch.should_poll: switch.update_ha_state(True) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service, descriptions.get(SERVICE_TURN_OFF), schema=SWITCH_SERVICE_SCHEMA) hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service, descriptions.get(SERVICE_TURN_ON), schema=SWITCH_SERVICE_SCHEMA) hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_switch_service, descriptions.get(SERVICE_TOGGLE), schema=SWITCH_SERVICE_SCHEMA) return True
def __init__(self, hass, app_name, topic, sandbox, cert_file): """Initialize APNS application.""" self.hass = hass self.app_name = app_name self.sandbox = sandbox self.certificate = cert_file self.yaml_path = hass.config.path(app_name + '_' + APNS_DEVICES) self.devices = {} self.device_states = {} self.topic = topic if os.path.isfile(self.yaml_path): self.devices = { str(key): ApnsDevice( str(key), value.get('name'), value.get('tracking_device_id'), value.get('disabled', False) ) for (key, value) in load_yaml_config_file(self.yaml_path).items() } tracking_ids = [ device.full_tracking_device_id for (key, device) in self.devices.items() if device.tracking_device_id is not None ] track_state_change( hass, tracking_ids, self.device_state_changed_listener)
def setup(hass, config): """Setup all groups found definded in the configuration.""" component = EntityComponent(_LOGGER, DOMAIN, hass) success = _process_config(hass, config, component) if not success: return False descriptions = conf_util.load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def reload_service_handler(service_call): """Remove all groups and load new ones from config.""" conf = component.prepare_reload() if conf is None: return _process_config(hass, conf, component) hass.services.register(DOMAIN, SERVICE_RELOAD, reload_service_handler, descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA) return True
def from_config_file(config_path, hass=None, verbose=False, daemon=False, skip_pip=True, log_rotate_days=None): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter if given, instantiates a new Home Assistant object if 'hass' is not given. """ if hass is None: hass = core.HomeAssistant() # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir mount_local_lib_path(config_dir) enable_logging(hass, verbose, daemon, log_rotate_days) try: config_dict = config_util.load_yaml_config_file(config_path) except HomeAssistantError: return None return from_config_dict(config_dict, hass, enable_log=False, skip_pip=skip_pip)
def register_services(hass): """Register all services for sonos devices.""" descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_GROUP_PLAYERS, _group_players_service, descriptions.get(SERVICE_GROUP_PLAYERS), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_UNJOIN, _unjoin_service, descriptions.get(SERVICE_UNJOIN), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_SNAPSHOT, _snapshot_service, descriptions.get(SERVICE_SNAPSHOT), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_RESTORE, _restore_service, descriptions.get(SERVICE_RESTORE), schema=SONOS_SCHEMA)
def test_scan_devices(self): """Test creating device info (MAC, name) from response. The created known_devices.yaml device info is compared to the DD-WRT Lan Status request response fixture. This effectively checks the data parsing functions. """ with requests_mock.Mocker() as mock_request: mock_request.register_uri( 'GET', r'http://%s/Status_Wireless.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Wireless.txt')) mock_request.register_uri( 'GET', r'http://%s/Status_Lan.live.asp' % TEST_HOST, text=load_fixture('Ddwrt_Status_Lan.txt')) with assert_setup_component(1, DOMAIN): assert setup_component( self.hass, DOMAIN, { DOMAIN: { CONF_PLATFORM: 'ddwrt', CONF_HOST: TEST_HOST, CONF_USERNAME: '******', CONF_PASSWORD: '******' } }) self.hass.block_till_done() path = self.hass.config.path(device_tracker.YAML_DEVICES) devices = config.load_yaml_config_file(path) for device in devices: self.assertIn(devices[device]['mac'], load_fixture('Ddwrt_Status_Lan.txt')) self.assertIn(slugify(devices[device]['name']), load_fixture('Ddwrt_Status_Lan.txt'))
def test_update_existing_device(self): """Test updating an existing device.""" config = { 'notify': { 'platform': 'apns', 'name': 'test_app', 'topic': 'testapp.appname', 'cert_file': 'test_app.pem' } } hass = get_test_home_assistant() devices_path = hass.config.path('test_app_apns.yaml') with open(devices_path, 'w+') as out: out.write('1234: {name: test device 1}\n') out.write('5678: {name: test device 2}\n') notify.setup(hass, config) self.assertTrue(hass.services.call('apns', 'test_app', {'push_id': '1234', 'name': 'updated device 1'}, blocking=True)) devices = {str(key): value for (key, value) in load_yaml_config_file(devices_path).items()} test_device_1 = devices.get('1234') test_device_2 = devices.get('5678') self.assertIsNotNone(test_device_1) self.assertIsNotNone(test_device_2) self.assertEqual('updated device 1', test_device_1.get('name')) os.remove(devices_path)
def test_load_yaml_config_converts_empty_files_to_dict(): """Test that loading an empty file returns an empty dict.""" create_file(YAML_PATH) assert isinstance(config_util.load_yaml_config_file(YAML_PATH), dict)
def setup(hass, config): """Setup Z-Wave. Will automatically load components to support devices found on the network. """ # pylint: disable=global-statement, import-error global NETWORK descriptions = conf_util.load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) try: import libopenzwave except ImportError: _LOGGER.error("You are missing required dependency Python Open " "Z-Wave. Please follow instructions at: " "https://home-assistant.io/components/zwave/") return False from pydispatch import dispatcher from openzwave.option import ZWaveOption from openzwave.network import ZWaveNetwork from openzwave.group import ZWaveGroup default_zwave_config_path = os.path.join( os.path.dirname(libopenzwave.__file__), 'config') # Load configuration use_debug = config[DOMAIN].get(CONF_DEBUG) autoheal = config[DOMAIN].get(CONF_AUTOHEAL) hass.data[DATA_DEVICE_CONFIG] = EntityValues( config[DOMAIN][CONF_DEVICE_CONFIG], config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN], config[DOMAIN][CONF_DEVICE_CONFIG_GLOB]) # Setup options options = ZWaveOption(config[DOMAIN].get(CONF_USB_STICK_PATH), user_path=hass.config.config_dir, config_path=config[DOMAIN].get( CONF_CONFIG_PATH, default_zwave_config_path)) options.set_console_output(use_debug) options.lock() NETWORK = ZWaveNetwork(options, autostart=False) if use_debug: def log_all(signal, value=None): """Log all the signals.""" print("") print("SIGNAL *****", signal) if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED, ZWaveNetwork.SIGNAL_VALUE_ADDED, ZWaveNetwork.SIGNAL_SCENE_EVENT, ZWaveNetwork.SIGNAL_NODE_EVENT, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED): pprint(_obj_to_dict(value)) print("") dispatcher.connect(log_all, weak=False) def value_added(node, value): """Called when a value is added to a node on the network.""" for (component, generic_device_class, specific_device_class, command_class, value_type, value_genre) in DISCOVERY_COMPONENTS: _LOGGER.debug("Component=%s Node_id=%s query start", component, node.node_id) if node.generic not in generic_device_class and \ None not in generic_device_class: _LOGGER.debug( "node.generic %s not None and in " "generic_device_class %s", node.generic, generic_device_class) continue if node.specific not in specific_device_class and \ None not in specific_device_class: _LOGGER.debug( "node.specific %s is not None and in " "specific_device_class %s", node.specific, specific_device_class) continue if value.command_class not in command_class and \ None not in command_class: _LOGGER.debug( "value.command_class %s is not None " "and in command_class %s", value.command_class, command_class) continue if value_type != value.type and value_type is not None: _LOGGER.debug("value.type %s != value_type %s", value.type, value_type) continue if value_genre != value.genre and value_genre is not None: _LOGGER.debug("value.genre %s != value_genre %s", value.genre, value_genre) continue # 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", node.node_id, node.generic, node.specific, value.command_class, value.type, value.genre, component) workaround_component = workaround.get_device_component_mapping( value) if workaround_component and workaround_component != component: if workaround_component == workaround.WORKAROUND_IGNORE: _LOGGER.info("Ignoring device %s due to workaround.", "{}.{}".format(component, object_id(value))) continue _LOGGER.debug("Using %s instead of %s", workaround_component, component) component = workaround_component name = "{}.{}".format(component, object_id(value)) node_config = hass.data[DATA_DEVICE_CONFIG].get(name) if node_config.get(CONF_IGNORED): _LOGGER.info("Ignoring device %s due to device settings.", name) return polling_intensity = convert( node_config.get(CONF_POLLING_INTENSITY), int) if polling_intensity: value.enable_poll(polling_intensity) else: value.disable_poll() discovery.load_platform( hass, component, DOMAIN, { const.ATTR_NODE_ID: node.node_id, const.ATTR_VALUE_ID: value.value_id, }, config) def scene_activated(node, scene_id): """Called when a scene is activated on any node in the network.""" hass.bus.fire( const.EVENT_SCENE_ACTIVATED, { ATTR_ENTITY_ID: _node_object_id(node), const.ATTR_OBJECT_ID: _node_object_id(node), const.ATTR_SCENE_ID: scene_id }) def node_event_activated(node, value): """Called when a nodeevent is activated on any node in the network.""" hass.bus.fire( const.EVENT_NODE_EVENT, { const.ATTR_OBJECT_ID: _node_object_id(node), const.ATTR_BASIC_LEVEL: value }) def network_ready(): """Called when all awake nodes have been queried.""" _LOGGER.info("Zwave network is ready for use. All awake nodes" " have been queried. Sleeping nodes will be" " queried when they awake.") hass.bus.fire(const.EVENT_NETWORK_READY) def network_complete(): """Called when all nodes on network have been queried.""" _LOGGER.info("Zwave network is complete. All nodes on the network" " have been queried") hass.bus.fire(const.EVENT_NETWORK_COMPLETE) dispatcher.connect(value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False) dispatcher.connect(scene_activated, ZWaveNetwork.SIGNAL_SCENE_EVENT, weak=False) dispatcher.connect(node_event_activated, ZWaveNetwork.SIGNAL_NODE_EVENT, weak=False) dispatcher.connect(network_ready, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, weak=False) dispatcher.connect(network_complete, ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED, weak=False) def add_node(service): """Switch into inclusion mode.""" _LOGGER.info("Zwave add_node have been initialized.") NETWORK.controller.add_node() def add_node_secure(service): """Switch into secure inclusion mode.""" _LOGGER.info("Zwave add_node_secure have been initialized.") NETWORK.controller.add_node(True) def remove_node(service): """Switch into exclusion mode.""" _LOGGER.info("Zwave remove_node have been initialized.") NETWORK.controller.remove_node() def cancel_command(service): """Cancel a running controller command.""" _LOGGER.info("Cancel running ZWave command.") NETWORK.controller.cancel_command() def heal_network(service): """Heal the network.""" _LOGGER.info("ZWave heal running.") NETWORK.heal() def soft_reset(service): """Soft reset the controller.""" _LOGGER.info("Zwave soft_reset have been initialized.") NETWORK.controller.soft_reset() def test_network(service): """Test the network by sending commands to all the nodes.""" _LOGGER.info("Zwave test_network have been initialized.") NETWORK.test() def stop_zwave(_service_or_event): """Stop Z-Wave network.""" _LOGGER.info("Stopping ZWave network.") NETWORK.stop() if hass.state == 'RUNNING': hass.bus.fire(const.EVENT_NETWORK_STOP) def rename_node(service): """Rename a node.""" state = hass.states.get(service.data.get(ATTR_ENTITY_ID)) node_id = state.attributes.get(const.ATTR_NODE_ID) node = NETWORK.nodes[node_id] name = service.data.get(const.ATTR_NAME) node.name = name _LOGGER.info("Renamed ZWave node %d to %s", node_id, name) def set_config_parameter(service): """Set a config parameter to a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = NETWORK.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) selection = service.data.get(const.ATTR_CONFIG_VALUE) size = service.data.get(const.ATTR_CONFIG_SIZE, 2) i = 0 for value in (node.get_values( class_id=const.COMMAND_CLASS_CONFIGURATION).values()): if value.index == param and value.type == const.TYPE_LIST: _LOGGER.debug('Values for parameter %s: %s', param, value.data_items) i = len(value.data_items) - 1 if i == 0: node.set_config_param(param, selection, size) else: if selection > i: _LOGGER.info( 'Config parameter selection does not exist!' ' Please check zwcfg_[home_id].xml in' ' your homeassistant config directory. ' ' Available selections are 0 to %s', i) return node.set_config_param(param, selection, size) _LOGGER.info( 'Setting config parameter %s on Node %s ' 'with selection %s and size=%s', param, node_id, selection, size) def print_config_parameter(service): """Print a config parameter from a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = NETWORK.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) _LOGGER.info("Config parameter %s on Node %s : %s", param, node_id, get_config_value(node, param)) def print_node(service): """Print all information about z-wave node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = NETWORK.nodes[node_id] nice_print_node(node) def set_wakeup(service): """Set wake-up interval of a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = NETWORK.nodes[node_id] value = service.data.get(const.ATTR_CONFIG_VALUE) if node.can_wake_up(): for value_id in node.get_values( class_id=const.COMMAND_CLASS_WAKE_UP): node.values[value_id].data = value _LOGGER.info("Node %s wake-up set to %d", node_id, value) else: _LOGGER.info("Node %s is not wakeable", node_id) def change_association(service): """Change an association in the zwave network.""" association_type = service.data.get(const.ATTR_ASSOCIATION) node_id = service.data.get(const.ATTR_NODE_ID) target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID) group = service.data.get(const.ATTR_GROUP) instance = service.data.get(const.ATTR_INSTANCE) node = ZWaveGroup(group, NETWORK, node_id) if association_type == 'add': node.add_association(target_node_id, instance) _LOGGER.info( "Adding association for node:%s in group:%s " "target node:%s, instance=%s", node_id, group, target_node_id, instance) if association_type == 'remove': node.remove_association(target_node_id, instance) _LOGGER.info( "Removing association for node:%s in group:%s " "target node:%s, instance=%s", node_id, group, target_node_id, instance) def start_zwave(_service_or_event): """Startup Z-Wave network.""" _LOGGER.info("Starting ZWave network.") NETWORK.start() hass.bus.fire(const.EVENT_NETWORK_START) # Need to be in STATE_AWAKED before talking to nodes. # Wait up to NETWORK_READY_WAIT_SECS seconds for the zwave network # to be ready. for i in range(const.NETWORK_READY_WAIT_SECS): _LOGGER.debug("network state: %d %s", NETWORK.state, NETWORK.state_str) if NETWORK.state >= NETWORK.STATE_AWAKED: _LOGGER.info("zwave ready after %d seconds", i) break time.sleep(1) else: _LOGGER.warning( "zwave not ready after %d seconds, continuing anyway", const.NETWORK_READY_WAIT_SECS) _LOGGER.info("final network state: %d %s", NETWORK.state, NETWORK.state_str) polling_interval = convert(config[DOMAIN].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("zwave polling interval set to %d ms", poll_interval) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zwave) # Register node services for Z-Wave network hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node, descriptions[const.SERVICE_ADD_NODE]) hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE, add_node_secure, descriptions[const.SERVICE_ADD_NODE_SECURE]) hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node, descriptions[const.SERVICE_REMOVE_NODE]) hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND, cancel_command, descriptions[const.SERVICE_CANCEL_COMMAND]) hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK, heal_network, descriptions[const.SERVICE_HEAL_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset, descriptions[const.SERVICE_SOFT_RESET]) hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK, test_network, descriptions[const.SERVICE_TEST_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK, stop_zwave, descriptions[const.SERVICE_STOP_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_START_NETWORK, start_zwave, descriptions[const.SERVICE_START_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_RENAME_NODE, rename_node, descriptions[const.SERVICE_RENAME_NODE], schema=RENAME_NODE_SCHEMA) hass.services.register( DOMAIN, const.SERVICE_SET_CONFIG_PARAMETER, set_config_parameter, descriptions[const.SERVICE_SET_CONFIG_PARAMETER], schema=SET_CONFIG_PARAMETER_SCHEMA) hass.services.register( DOMAIN, const.SERVICE_PRINT_CONFIG_PARAMETER, print_config_parameter, descriptions[const.SERVICE_PRINT_CONFIG_PARAMETER], schema=PRINT_CONFIG_PARAMETER_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_CHANGE_ASSOCIATION, change_association, descriptions[const.SERVICE_CHANGE_ASSOCIATION], schema=CHANGE_ASSOCIATION_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_SET_WAKEUP, set_wakeup, descriptions[const.SERVICE_SET_WAKEUP], schema=SET_WAKEUP_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_PRINT_NODE, print_node, descriptions[const.SERVICE_PRINT_NODE], schema=PRINT_NODE_SCHEMA) # Setup autoheal if autoheal: _LOGGER.info("ZWave network autoheal is enabled.") track_time_change(hass, heal_network, hour=0, minute=0, second=0) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave) return True
def setup(hass, config): """Expose light control via statemachine and services.""" component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_LIGHTS) component.setup(config) # Load built-in profiles and custom profiles profile_paths = [ os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE), hass.config.path(LIGHT_PROFILES_FILE) ] profiles = {} for profile_path in profile_paths: if not os.path.isfile(profile_path): continue with open(profile_path) as inp: reader = csv.reader(inp) # Skip the header next(reader, None) try: for rec in reader: profile, color_x, color_y, brightness = PROFILE_SCHEMA(rec) profiles[profile] = (color_x, color_y, brightness) except vol.MultipleInvalid as ex: _LOGGER.error("Error parsing light profile from %s: %s", profile_path, ex) return False def handle_light_service(service): """Hande a turn light on or off service call.""" # Get the validated data params = service.data.copy() # Convert the entity ids to valid light ids target_lights = component.extract_from_service(service) params.pop(ATTR_ENTITY_ID, None) service_fun = None if service.service == SERVICE_TURN_OFF: service_fun = 'turn_off' elif service.service == SERVICE_TOGGLE: service_fun = 'toggle' if service_fun: for light in target_lights: getattr(light, service_fun)(**params) for light in target_lights: if light.should_poll: light.update_ha_state(True) return # Processing extra data for turn light on request. profile = profiles.get(params.pop(ATTR_PROFILE, None)) if profile: params.setdefault(ATTR_XY_COLOR, profile[:2]) params.setdefault(ATTR_BRIGHTNESS, profile[2]) color_name = params.pop(ATTR_COLOR_NAME, None) if color_name is not None: params[ATTR_RGB_COLOR] = color_util.color_name_to_rgb(color_name) for light in target_lights: light.turn_on(**params) for light in target_lights: if light.should_poll: light.update_ha_state(True) # Listen for light on and light off service calls. descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service, descriptions.get(SERVICE_TURN_ON), schema=LIGHT_TURN_ON_SCHEMA) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service, descriptions.get(SERVICE_TURN_OFF), schema=LIGHT_TURN_OFF_SCHEMA) hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_light_service, descriptions.get(SERVICE_TOGGLE), schema=LIGHT_TOGGLE_SCHEMA) return True
def setup(hass, config): """Track states and offer events for media_players.""" component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) component.setup(config) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def media_player_service_handler(service): """Map services to methods on MediaPlayerDevice.""" method = SERVICE_TO_METHOD[service.service] for player in component.extract_from_service(service): getattr(player, method)() if player.should_poll: player.update_ha_state(True) for service in SERVICE_TO_METHOD: hass.services.register(DOMAIN, service, media_player_service_handler, descriptions.get(service), schema=MEDIA_PLAYER_SCHEMA) def volume_set_service(service): """Set specified volume on the media player.""" volume = service.data.get(ATTR_MEDIA_VOLUME_LEVEL) for player in component.extract_from_service(service): player.set_volume_level(volume) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service, descriptions.get(SERVICE_VOLUME_SET), schema=MEDIA_PLAYER_SET_VOLUME_SCHEMA) def volume_mute_service(service): """Mute (true) or unmute (false) the media player.""" mute = service.data.get(ATTR_MEDIA_VOLUME_MUTED) for player in component.extract_from_service(service): player.mute_volume(mute) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service, descriptions.get(SERVICE_VOLUME_MUTE), schema=MEDIA_PLAYER_MUTE_VOLUME_SCHEMA) def media_seek_service(service): """Seek to a position.""" position = service.data.get(ATTR_MEDIA_SEEK_POSITION) for player in component.extract_from_service(service): player.media_seek(position) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service, descriptions.get(SERVICE_MEDIA_SEEK), schema=MEDIA_PLAYER_MEDIA_SEEK_SCHEMA) def select_source_service(service): """Change input to selected source.""" input_source = service.data.get(ATTR_INPUT_SOURCE) for player in component.extract_from_service(service): player.select_source(input_source) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_SELECT_SOURCE, select_source_service, descriptions.get(SERVICE_SELECT_SOURCE), schema=MEDIA_PLAYER_SELECT_SOURCE_SCHEMA) def play_media_service(service): """Play specified media_id on the media player.""" media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE) media_id = service.data.get(ATTR_MEDIA_CONTENT_ID) enqueue = service.data.get(ATTR_MEDIA_ENQUEUE) kwargs = { ATTR_MEDIA_ENQUEUE: enqueue, } for player in component.extract_from_service(service): player.play_media(media_type, media_id, **kwargs) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_PLAY_MEDIA, play_media_service, descriptions.get(SERVICE_PLAY_MEDIA), schema=MEDIA_PLAYER_PLAY_MEDIA_SCHEMA) return True
def setup(hass, config): """Common setup for Axis devices.""" def _shutdown(call): # pylint: disable=unused-argument """Stop the event stream on shutdown.""" for serialnumber, device in AXIS_DEVICES.items(): _LOGGER.info("Stopping event stream for %s.", serialnumber) device.stop() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) def axis_device_discovered(service, discovery_info): """Called when axis devices has been found.""" host = discovery_info[CONF_HOST] name = discovery_info['hostname'] serialnumber = discovery_info['properties']['macaddress'] if serialnumber not in AXIS_DEVICES: config_file = _read_config(hass) if serialnumber in config_file: # Device config previously saved to file try: device_config = DEVICE_SCHEMA(config_file[serialnumber]) device_config[CONF_HOST] = host except vol.Invalid as err: _LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err) return False if not setup_device(hass, config, device_config): _LOGGER.error("Couldn\'t set up %s", device_config[CONF_NAME]) else: # New device, create configuration request for UI request_configuration(hass, config, name, host, serialnumber) else: # Device already registered, but on a different IP device = AXIS_DEVICES[serialnumber] device.config.host = host dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host) # Register discovery service discovery.listen(hass, SERVICE_AXIS, axis_device_discovered) if DOMAIN in config: for device in config[DOMAIN]: device_config = config[DOMAIN][device] if CONF_NAME not in device_config: device_config[CONF_NAME] = device if not setup_device(hass, config, device_config): _LOGGER.error("Couldn\'t set up %s", device_config[CONF_NAME]) # Services to communicate with device. descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def vapix_service(call): """Service to send a message.""" for _, device in AXIS_DEVICES.items(): if device.name == call.data[CONF_NAME]: response = device.vapix.do_request(call.data[SERVICE_CGI], call.data[SERVICE_ACTION], call.data[SERVICE_PARAM]) hass.bus.fire(SERVICE_VAPIX_CALL_RESPONSE, response) return True _LOGGER.info("Couldn\'t find device %s", call.data[CONF_NAME]) return False # Register service with Home Assistant. hass.services.register(DOMAIN, SERVICE_VAPIX_CALL, vapix_service, descriptions[DOMAIN][SERVICE_VAPIX_CALL], schema=SERVICE_SCHEMA) return True
def get_device(hass, node, value, **kwargs): """Create zwave entity device.""" descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) def set_usercode(service): """Set the usercode to index X on the lock.""" node_id = service.data.get(zwave.const.ATTR_NODE_ID) lock_node = zwave.NETWORK.nodes[node_id] code_slot = service.data.get(ATTR_CODE_SLOT) usercode = service.data.get(ATTR_USERCODE) for value in lock_node.get_values( class_id=zwave.const.COMMAND_CLASS_USER_CODE).values(): if value.index != code_slot: continue if len(str(usercode)) > 4: _LOGGER.error( 'Invalid code provided: (%s)' ' usercode must %s or less digits', usercode, len(value.data)) value.data = str(usercode) break def get_usercode(service): """Get a usercode at index X on the lock.""" node_id = service.data.get(zwave.const.ATTR_NODE_ID) lock_node = zwave.NETWORK.nodes[node_id] code_slot = service.data.get(ATTR_CODE_SLOT) for value in lock_node.get_values( class_id=zwave.const.COMMAND_CLASS_USER_CODE).values(): if value.index != code_slot: continue _LOGGER.info('Usercode at slot %s is: %s', value.index, value.data) break def clear_usercode(service): """Set usercode to slot X on the lock.""" node_id = service.data.get(zwave.const.ATTR_NODE_ID) lock_node = zwave.NETWORK.nodes[node_id] code_slot = service.data.get(ATTR_CODE_SLOT) data = '' for value in lock_node.get_values( class_id=zwave.const.COMMAND_CLASS_USER_CODE).values(): if value.index != code_slot: continue for i in range(len(value.data)): data += '\0' i += 1 _LOGGER.debug('Data to clear lock: %s', data) value.data = data _LOGGER.info('Usercode at slot %s is cleared', value.index) break if node.has_command_class(zwave.const.COMMAND_CLASS_USER_CODE): hass.services.register(DOMAIN, SERVICE_SET_USERCODE, set_usercode, descriptions.get(SERVICE_SET_USERCODE), schema=SET_USERCODE_SCHEMA) hass.services.register(DOMAIN, SERVICE_GET_USERCODE, get_usercode, descriptions.get(SERVICE_GET_USERCODE), schema=GET_USERCODE_SCHEMA) hass.services.register(DOMAIN, SERVICE_CLEAR_USERCODE, clear_usercode, descriptions.get(SERVICE_CLEAR_USERCODE), schema=CLEAR_USERCODE_SCHEMA) return ZwaveLock(value)
def test_unhashable_key(): """Test an unhashable key.""" files = {YAML_CONFIG_FILE: "message:\n {{ states.state }}"} with pytest.raises(HomeAssistantError), patch_yaml_files(files): load_yaml_config_file(YAML_CONFIG_FILE)
def setup(hass, config): """Setup Z-Wave. Will automatically load components to support devices found on the network. """ descriptions = conf_util.load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) try: import libopenzwave except ImportError: _LOGGER.error("You are missing required dependency Python Open " "Z-Wave. Please follow instructions at: " "https://home-assistant.io/components/zwave/") return False from pydispatch import dispatcher # pylint: disable=import-error from openzwave.option import ZWaveOption from openzwave.network import ZWaveNetwork from openzwave.group import ZWaveGroup default_zwave_config_path = os.path.join( os.path.dirname(libopenzwave.__file__), 'config') # Load configuration use_debug = config[DOMAIN].get(CONF_DEBUG) autoheal = config[DOMAIN].get(CONF_AUTOHEAL) device_config = EntityValues(config[DOMAIN][CONF_DEVICE_CONFIG], config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN], config[DOMAIN][CONF_DEVICE_CONFIG_GLOB]) # Setup options options = ZWaveOption(config[DOMAIN].get(CONF_USB_STICK_PATH), user_path=hass.config.config_dir, config_path=config[DOMAIN].get( CONF_CONFIG_PATH, default_zwave_config_path)) options.set_console_output(use_debug) options.lock() network = hass.data[ZWAVE_NETWORK] = ZWaveNetwork(options, autostart=False) hass.data[DATA_ZWAVE_DICT] = {} if use_debug: # pragma: no cover def log_all(signal, value=None): """Log all the signals.""" print("") print("SIGNAL *****", signal) if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED, ZWaveNetwork.SIGNAL_VALUE_ADDED, ZWaveNetwork.SIGNAL_SCENE_EVENT, ZWaveNetwork.SIGNAL_NODE_EVENT, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED): pprint(_obj_to_dict(value)) print("") dispatcher.connect(log_all, weak=False) discovered_values = [] def value_added(node, value): """Called when a value is added to a node on the network.""" # Check if this value should be tracked by an existing entity for values in discovered_values: values.check_value(value) for schema in DISCOVERY_SCHEMAS: if not check_node_schema(node, schema): continue if not check_value_schema( value, schema[const.DISC_VALUES][const.DISC_PRIMARY]): continue values = ZWaveDeviceEntityValues(hass, schema, value, config, device_config) discovered_values.append(values) component = EntityComponent(_LOGGER, DOMAIN, hass) def node_added(node): """Called when a node is added on the network.""" entity = ZWaveNodeEntity(node, network) node_config = device_config.get(entity.entity_id) if node_config.get(CONF_IGNORED): _LOGGER.info("Ignoring node entity %s due to device settings.", entity.entity_id) return component.add_entities([entity]) def scene_activated(node, scene_id): """Called when a scene is activated on any node in the network.""" hass.bus.fire( const.EVENT_SCENE_ACTIVATED, { ATTR_ENTITY_ID: _node_object_id(node), const.ATTR_OBJECT_ID: _node_object_id(node), const.ATTR_SCENE_ID: scene_id }) def node_event_activated(node, value): """Called when a nodeevent is activated on any node in the network.""" hass.bus.fire( const.EVENT_NODE_EVENT, { const.ATTR_OBJECT_ID: _node_object_id(node), const.ATTR_BASIC_LEVEL: value }) def network_ready(): """Called when all awake nodes have been queried.""" _LOGGER.info("Zwave network is ready for use. All awake nodes" " have been queried. Sleeping nodes will be" " queried when they awake.") hass.bus.fire(const.EVENT_NETWORK_READY) def network_complete(): """Called when all nodes on network have been queried.""" _LOGGER.info("Zwave network is complete. All nodes on the network" " have been queried") hass.bus.fire(const.EVENT_NETWORK_COMPLETE) dispatcher.connect(value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False) dispatcher.connect(node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False) dispatcher.connect(scene_activated, ZWaveNetwork.SIGNAL_SCENE_EVENT, weak=False) dispatcher.connect(node_event_activated, ZWaveNetwork.SIGNAL_NODE_EVENT, weak=False) dispatcher.connect(network_ready, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, weak=False) dispatcher.connect(network_complete, ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED, weak=False) def add_node(service): """Switch into inclusion mode.""" _LOGGER.info("Zwave add_node have been initialized.") network.controller.add_node() def add_node_secure(service): """Switch into secure inclusion mode.""" _LOGGER.info("Zwave add_node_secure have been initialized.") network.controller.add_node(True) def remove_node(service): """Switch into exclusion mode.""" _LOGGER.info("Zwave remove_node have been initialized.") network.controller.remove_node() def cancel_command(service): """Cancel a running controller command.""" _LOGGER.info("Cancel running ZWave command.") network.controller.cancel_command() def heal_network(service): """Heal the network.""" _LOGGER.info("ZWave heal running.") network.heal() def soft_reset(service): """Soft reset the controller.""" _LOGGER.info("Zwave soft_reset have been initialized.") network.controller.soft_reset() def test_network(service): """Test the network by sending commands to all the nodes.""" _LOGGER.info("Zwave test_network have been initialized.") network.test() def stop_network(_service_or_event): """Stop Z-Wave network.""" _LOGGER.info("Stopping ZWave network.") network.stop() if hass.state == CoreState.running: hass.bus.fire(const.EVENT_NETWORK_STOP) def rename_node(service): """Rename a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = hass.data[ZWAVE_NETWORK].nodes[node_id] name = service.data.get(const.ATTR_NAME) node.name = name _LOGGER.info("Renamed ZWave node %d to %s", node_id, name) def remove_failed_node(service): """Remove failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) _LOGGER.info('Trying to remove zwave node %d', node_id) network.controller.remove_failed_node(node_id) def replace_failed_node(service): """Replace failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) _LOGGER.info('Trying to replace zwave node %d', node_id) network.controller.replace_failed_node(node_id) def set_config_parameter(service): """Set a config parameter to a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) selection = service.data.get(const.ATTR_CONFIG_VALUE) size = service.data.get(const.ATTR_CONFIG_SIZE, 2) i = 0 for value in (node.get_values( class_id=const.COMMAND_CLASS_CONFIGURATION).values()): if value.index == param and value.type == const.TYPE_LIST: _LOGGER.debug('Values for parameter %s: %s', param, value.data_items) i = len(value.data_items) - 1 if i == 0: node.set_config_param(param, selection, size) else: if selection > i: _LOGGER.info( 'Config parameter selection does not exist!' ' Please check zwcfg_[home_id].xml in' ' your homeassistant config directory. ' ' Available selections are 0 to %s', i) return node.set_config_param(param, selection, size) _LOGGER.info( 'Setting config parameter %s on Node %s ' 'with selection %s and size=%s', param, node_id, selection, size) def print_config_parameter(service): """Print a config parameter from a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) _LOGGER.info("Config parameter %s on Node %s : %s", param, node_id, get_config_value(node, param)) def print_node(service): """Print all information about z-wave node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] nice_print_node(node) def set_wakeup(service): """Set wake-up interval of a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] value = service.data.get(const.ATTR_CONFIG_VALUE) if node.can_wake_up(): for value_id in node.get_values( class_id=const.COMMAND_CLASS_WAKE_UP): node.values[value_id].data = value _LOGGER.info("Node %s wake-up set to %d", node_id, value) else: _LOGGER.info("Node %s is not wakeable", node_id) def change_association(service): """Change an association in the zwave network.""" association_type = service.data.get(const.ATTR_ASSOCIATION) node_id = service.data.get(const.ATTR_NODE_ID) target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID) group = service.data.get(const.ATTR_GROUP) instance = service.data.get(const.ATTR_INSTANCE) node = ZWaveGroup(group, network, node_id) if association_type == 'add': node.add_association(target_node_id, instance) _LOGGER.info( "Adding association for node:%s in group:%s " "target node:%s, instance=%s", node_id, group, target_node_id, instance) if association_type == 'remove': node.remove_association(target_node_id, instance) _LOGGER.info( "Removing association for node:%s in group:%s " "target node:%s, instance=%s", node_id, group, target_node_id, instance) @asyncio.coroutine def async_refresh_entity(service): """Refresh values that specific entity depends on.""" entity_id = service.data.get(ATTR_ENTITY_ID) async_dispatcher_send(hass, SIGNAL_REFRESH_ENTITY_FORMAT.format(entity_id)) def refresh_node(service): """Refresh all node info.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] node.refresh_info() def start_zwave(_service_or_event): """Startup Z-Wave network.""" _LOGGER.info("Starting ZWave network.") network.start() hass.bus.fire(const.EVENT_NETWORK_START) # Need to be in STATE_AWAKED before talking to nodes. # Wait up to NETWORK_READY_WAIT_SECS seconds for the zwave network # to be ready. for i in range(const.NETWORK_READY_WAIT_SECS): _LOGGER.debug("network state: %d %s", hass.data[ZWAVE_NETWORK].state, network.state_str) if network.state >= network.STATE_AWAKED: _LOGGER.info("zwave ready after %d seconds", i) break time.sleep(1) else: _LOGGER.warning( "zwave not ready after %d seconds, continuing anyway", const.NETWORK_READY_WAIT_SECS) _LOGGER.info("final network state: %d %s", network.state, network.state_str) polling_interval = convert(config[DOMAIN].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("zwave polling interval set to %d ms", poll_interval) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_network) # Register node services for Z-Wave network hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node, descriptions[const.SERVICE_ADD_NODE]) hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE, add_node_secure, descriptions[const.SERVICE_ADD_NODE_SECURE]) hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node, descriptions[const.SERVICE_REMOVE_NODE]) hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND, cancel_command, descriptions[const.SERVICE_CANCEL_COMMAND]) hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK, heal_network, descriptions[const.SERVICE_HEAL_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset, descriptions[const.SERVICE_SOFT_RESET]) hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK, test_network, descriptions[const.SERVICE_TEST_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK, stop_network, descriptions[const.SERVICE_STOP_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_START_NETWORK, start_zwave, descriptions[const.SERVICE_START_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_RENAME_NODE, rename_node, descriptions[const.SERVICE_RENAME_NODE], schema=RENAME_NODE_SCHEMA) hass.services.register( DOMAIN, const.SERVICE_SET_CONFIG_PARAMETER, set_config_parameter, descriptions[const.SERVICE_SET_CONFIG_PARAMETER], schema=SET_CONFIG_PARAMETER_SCHEMA) hass.services.register( DOMAIN, const.SERVICE_PRINT_CONFIG_PARAMETER, print_config_parameter, descriptions[const.SERVICE_PRINT_CONFIG_PARAMETER], schema=PRINT_CONFIG_PARAMETER_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REMOVE_FAILED_NODE, remove_failed_node, descriptions[const.SERVICE_REMOVE_FAILED_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REPLACE_FAILED_NODE, replace_failed_node, descriptions[const.SERVICE_REPLACE_FAILED_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_CHANGE_ASSOCIATION, change_association, descriptions[const.SERVICE_CHANGE_ASSOCIATION], schema=CHANGE_ASSOCIATION_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_SET_WAKEUP, set_wakeup, descriptions[const.SERVICE_SET_WAKEUP], schema=SET_WAKEUP_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_PRINT_NODE, print_node, descriptions[const.SERVICE_PRINT_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REFRESH_ENTITY, async_refresh_entity, descriptions[const.SERVICE_REFRESH_ENTITY], schema=REFRESH_ENTITY_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REFRESH_NODE, refresh_node, descriptions[const.SERVICE_REFRESH_NODE], schema=NODE_SERVICE_SCHEMA) # Setup autoheal if autoheal: _LOGGER.info("ZWave network autoheal is enabled.") track_time_change(hass, heal_network, hour=0, minute=0, second=0) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave) if 'frontend' in hass.config.components: register_built_in_panel(hass, 'zwave', 'Z-Wave', 'mdi:nfc') return True
def setup(hass, config): """Setup thermostats.""" component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) component.setup(config) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def away_mode_set_service(service): """Set away mode on target thermostats.""" target_thermostats = component.extract_from_service(service) away_mode = service.data[ATTR_AWAY_MODE] for thermostat in target_thermostats: if away_mode: thermostat.turn_away_mode_on() else: thermostat.turn_away_mode_off() thermostat.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_SET_AWAY_MODE, away_mode_set_service, descriptions.get(SERVICE_SET_AWAY_MODE), schema=SET_AWAY_MODE_SCHEMA) def temperature_set_service(service): """Set temperature on the target thermostats.""" target_thermostats = component.extract_from_service(service) temperature = service.data[ATTR_TEMPERATURE] for thermostat in target_thermostats: thermostat.set_temperature( convert(temperature, hass.config.temperature_unit, thermostat.unit_of_measurement)) thermostat.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_SET_TEMPERATURE, temperature_set_service, descriptions.get(SERVICE_SET_TEMPERATURE), schema=SET_TEMPERATURE_SCHEMA) def fan_mode_set_service(service): """Set fan mode on target thermostats.""" target_thermostats = component.extract_from_service(service) fan_mode = service.data[ATTR_FAN] for thermostat in target_thermostats: if fan_mode: thermostat.turn_fan_on() else: thermostat.turn_fan_off() thermostat.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_SET_FAN_MODE, fan_mode_set_service, descriptions.get(SERVICE_SET_FAN_MODE), schema=SET_FAN_MODE_SCHEMA) def hvac_mode_set_service(service): """Set hvac mode on target thermostats.""" target_thermostats = component.extract_from_service(service) hvac_mode = service.data[ATTR_HVAC_MODE] for thermostat in target_thermostats: thermostat.set_hvac_mode(hvac_mode) thermostat.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_SET_HVAC_MODE, hvac_mode_set_service, descriptions.get(SERVICE_SET_HVAC_MODE), schema=SET_HVAC_MODE_SCHEMA) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Sonos platform.""" import soco if DATA_SONOS not in hass.data: hass.data[DATA_SONOS] = [] advertise_addr = config.get(CONF_ADVERTISE_ADDR, None) if advertise_addr: soco.config.EVENT_ADVERTISE_IP = advertise_addr if discovery_info: player = soco.SoCo(discovery_info.get('host')) # if device already exists by config if player.uid in [x.unique_id for x in hass.data[DATA_SONOS]]: return if player.is_visible: device = SonosDevice(player) add_devices([device], True) hass.data[DATA_SONOS].append(device) if len(hass.data[DATA_SONOS]) > 1: return else: players = None hosts = config.get(CONF_HOSTS, None) if hosts: # Support retro compatibility with comma separated list of hosts # from config hosts = hosts[0] if len(hosts) == 1 else hosts hosts = hosts.split(',') if isinstance(hosts, str) else hosts players = [] for host in hosts: players.append(soco.SoCo(socket.gethostbyname(host))) if not players: players = soco.discover( interface_addr=config.get(CONF_INTERFACE_ADDR)) if not players: _LOGGER.warning("No Sonos speakers found") return # Add coordinators first so they can be queried by slaves coordinators = [SonosDevice(p) for p in players if p.is_coordinator] slaves = [SonosDevice(p) for p in players if not p.is_coordinator] hass.data[DATA_SONOS] = coordinators + slaves if coordinators: add_devices(coordinators, True) if slaves: add_devices(slaves, True) _LOGGER.info("Added %s Sonos speakers", len(players)) descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) def service_handle(service): """Handle for services.""" entity_ids = service.data.get('entity_id') if entity_ids: devices = [ device for device in hass.data[DATA_SONOS] if device.entity_id in entity_ids ] else: devices = hass.data[DATA_SONOS] for device in devices: if service.service == SERVICE_JOIN: if device.entity_id != service.data[ATTR_MASTER]: device.join(service.data[ATTR_MASTER]) elif service.service == SERVICE_UNJOIN: device.unjoin() elif service.service == SERVICE_SNAPSHOT: device.snapshot(service.data[ATTR_WITH_GROUP]) elif service.service == SERVICE_RESTORE: device.restore(service.data[ATTR_WITH_GROUP]) elif service.service == SERVICE_SET_TIMER: device.set_sleep_timer(service.data[ATTR_SLEEP_TIME]) elif service.service == SERVICE_CLEAR_TIMER: device.clear_sleep_timer() elif service.service == SERVICE_UPDATE_ALARM: device.update_alarm(**service.data) device.schedule_update_ha_state(True) hass.services.register(DOMAIN, SERVICE_JOIN, service_handle, descriptions.get(SERVICE_JOIN), schema=SONOS_JOIN_SCHEMA) hass.services.register(DOMAIN, SERVICE_UNJOIN, service_handle, descriptions.get(SERVICE_UNJOIN), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_SNAPSHOT, service_handle, descriptions.get(SERVICE_SNAPSHOT), schema=SONOS_STATES_SCHEMA) hass.services.register(DOMAIN, SERVICE_RESTORE, service_handle, descriptions.get(SERVICE_RESTORE), schema=SONOS_STATES_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_TIMER, service_handle, descriptions.get(SERVICE_SET_TIMER), schema=SONOS_SET_TIMER_SCHEMA) hass.services.register(DOMAIN, SERVICE_CLEAR_TIMER, service_handle, descriptions.get(SERVICE_CLEAR_TIMER), schema=SONOS_SCHEMA) hass.services.register(DOMAIN, SERVICE_UPDATE_ALARM, service_handle, descriptions.get(SERVICE_UPDATE_ALARM), schema=SONOS_UPDATE_ALARM_SCHEMA)
def test_duplicate_key(self): """Test duplicate dict keys.""" files = {YAML_CONFIG_FILE: 'key: thing1\nkey: thing2'} with self.assertRaises(HomeAssistantError): with patch_yaml_files(files): load_yaml_config_file(YAML_CONFIG_FILE)
def setup(hass, config): """Start the MQTT protocol service.""" # pylint: disable=too-many-locals conf = config.get(DOMAIN, {}) client_id = conf.get(CONF_CLIENT_ID) keepalive = conf.get(CONF_KEEPALIVE) broker_config = _setup_server(hass, config) broker_in_conf = CONF_BROKER in conf # Only auto config if no server config was passed in if broker_config and CONF_EMBEDDED not in conf: broker, port, username, password, certificate, protocol = broker_config # Embedded broker doesn't have some ssl variables client_key, client_cert, tls_insecure = None, None, None elif not broker_config and not broker_in_conf: _LOGGER.error('Unable to start broker and auto-configure MQTT.') return False if broker_in_conf: broker = conf[CONF_BROKER] port = conf[CONF_PORT] username = conf.get(CONF_USERNAME) password = conf.get(CONF_PASSWORD) certificate = conf.get(CONF_CERTIFICATE) client_key = conf.get(CONF_CLIENT_KEY) client_cert = conf.get(CONF_CLIENT_CERT) tls_insecure = conf.get(CONF_TLS_INSECURE) protocol = conf[CONF_PROTOCOL] # For cloudmqtt.com, secured connection, auto fill in certificate if certificate is None and 19999 < port < 30000 and \ broker.endswith('.cloudmqtt.com'): certificate = os.path.join(os.path.dirname(__file__), 'addtrustexternalcaroot.crt') global MQTT_CLIENT try: MQTT_CLIENT = MQTT(hass, broker, port, client_id, keepalive, username, password, certificate, client_key, client_cert, tls_insecure, protocol) except socket.error: _LOGGER.exception("Can't connect to the broker. " "Please check your settings and the broker " "itself.") return False def stop_mqtt(event): """Stop MQTT component.""" MQTT_CLIENT.stop() def start_mqtt(event): """Launch MQTT component when Home Assistant starts up.""" MQTT_CLIENT.start() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_mqtt) def publish_service(call): """Handle MQTT publish service calls.""" msg_topic = call.data[ATTR_TOPIC] payload = call.data.get(ATTR_PAYLOAD) payload_template = call.data.get(ATTR_PAYLOAD_TEMPLATE) qos = call.data[ATTR_QOS] retain = call.data[ATTR_RETAIN] try: payload = (payload if payload_template is None else template.render(hass, payload_template)) or '' except template.jinja2.TemplateError as exc: _LOGGER.error( "Unable to publish to '%s': rendering payload template of " "'%s' failed because %s.", msg_topic, payload_template, exc) return MQTT_CLIENT.publish(msg_topic, payload, qos, retain) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_mqtt) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_PUBLISH, publish_service, descriptions.get(SERVICE_PUBLISH), schema=MQTT_PUBLISH_SCHEMA) return True
def check_ha_config_file(hass): """Check if Home Assistant configuration file is valid.""" config_dir = hass.config.config_dir result = HomeAssistantConfig() def _pack_error(package, component, config, message): """Handle errors from packages: _log_pkg_error.""" message = "Package {} setup failed. Component {} {}".format( package, component, message) domain = 'homeassistant.packages.{}.{}'.format(package, component) pack_config = core_config[CONF_PACKAGES].get(package, config) result.add_error(message, domain, pack_config) def _comp_error(ex, domain, config): """Handle errors from components: async_log_exception.""" result.add_error(_format_config_error(ex, domain, config), domain, config) # Load configuration.yaml try: config_path = find_config_file(config_dir) if not config_path: return result.add_error("File configuration.yaml not found.") config = load_yaml_config_file(config_path) except HomeAssistantError as err: return result.add_error("Error loading {}: {}".format( config_path, err)) finally: yaml.clear_secret_cache() # Extract and validate core [homeassistant] config try: core_config = config.pop(CONF_CORE, {}) core_config = CORE_CONFIG_SCHEMA(core_config) result[CONF_CORE] = core_config except vol.Invalid as err: result.add_error(err, CONF_CORE, core_config) core_config = {} # Merge packages merge_packages_config(hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error) del core_config[CONF_PACKAGES] # Ensure we have no None values after merge for key, value in config.items(): if not value: config[key] = {} # Filter out repeating config sections components = set(key.split(' ')[0] for key in config.keys()) # Process and validate config for domain in components: component = loader.get_component(hass, domain) if not component: result.add_error("Component not found: {}".format(domain)) continue if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) result[domain] = config[domain] except vol.Invalid as ex: _comp_error(ex, domain, config) continue if not hasattr(component, 'PLATFORM_SCHEMA'): continue platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: _comp_error(ex, domain, config) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = loader.get_platform(hass, domain, p_name) if platform is None: result.add_error("Platform not found: {}.{}".format( domain, p_name)) continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: _comp_error(ex, '{}.{}'.format(domain, p_name), p_validated) continue platforms.append(p_validated) # Remove config for current component and add validated config back in. for filter_comp in extract_domain_configs(config, domain): del config[filter_comp] result[domain] = platforms return result
config) def autosetup_ihc_products(hass: HomeAssistant, config, ihc_controller, controller_id): """Auto setup of IHC products from the IHC project file.""" if not (project_xml := ihc_controller.get_project()): _LOGGER.error("Unable to read project from IHC controller") return False project = ElementTree.fromstring(project_xml) # If an auto setup file exist in the configuration it will override yaml_path = hass.config.path(AUTO_SETUP_YAML) if not os.path.isfile(yaml_path): yaml_path = os.path.join(os.path.dirname(__file__), AUTO_SETUP_YAML) yaml = load_yaml_config_file(yaml_path) try: auto_setup_conf = AUTO_SETUP_SCHEMA(yaml) except vol.Invalid as exception: _LOGGER.error("Invalid IHC auto setup data: %s", exception) return False groups = project.findall(".//group") for platform in PLATFORMS: platform_setup = auto_setup_conf[platform] discovery_info = get_discovery_info(platform_setup, groups, controller_id) if discovery_info: discovery.load_platform(hass, platform, DOMAIN, discovery_info, config)
def test_representing_yaml_loaded_data(): """Test we can represent YAML loaded data.""" files = {YAML_CONFIG_FILE: 'key: [1, "2", 3]'} with patch_yaml_files(files): data = load_yaml_config_file(YAML_CONFIG_FILE) assert yaml.dump(data) == "key:\n- 1\n- '2'\n- 3\n"
def setup(hass, config): """Set up the Wink component.""" import pywink from pubnubsubhandler import PubNubSubscriptionHandler descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) if hass.data.get(DOMAIN) is None: hass.data[DOMAIN] = { 'unique_ids': [], 'entities': {}, 'oauth': {}, 'configuring': {}, 'pubnub': None, 'configurator': False } if config.get(DOMAIN) is not None: client_id = config[DOMAIN].get(ATTR_CLIENT_ID) client_secret = config[DOMAIN].get(ATTR_CLIENT_SECRET) email = config[DOMAIN].get(CONF_EMAIL) password = config[DOMAIN].get(CONF_PASSWORD) local_control = config[DOMAIN].get(CONF_LOCAL_CONTROL) else: client_id = None client_secret = None email = None password = None local_control = None hass.data[DOMAIN]['configurator'] = True if None not in [client_id, client_secret]: _LOGGER.info("Using legacy oauth authentication") if not local_control: pywink.disable_local_control() hass.data[DOMAIN]["oauth"]["client_id"] = client_id hass.data[DOMAIN]["oauth"]["client_secret"] = client_secret hass.data[DOMAIN]["oauth"]["email"] = email hass.data[DOMAIN]["oauth"]["password"] = password pywink.legacy_set_wink_credentials(email, password, client_id, client_secret) else: _LOGGER.info("Using oauth authentication") if not local_control: pywink.disable_local_control() config_path = hass.config.path(WINK_CONFIG_FILE) if os.path.isfile(config_path): config_file = load_json(config_path) if config_file == DEFAULT_CONFIG: _request_app_setup(hass, config) return True # else move on because the user modified the file else: save_json(config_path, DEFAULT_CONFIG) _request_app_setup(hass, config) return True if DOMAIN in hass.data[DOMAIN]['configuring']: _configurator = hass.data[DOMAIN]['configuring'] hass.components.configurator.request_done(_configurator.pop( DOMAIN)) # Using oauth access_token = config_file.get(ATTR_ACCESS_TOKEN) refresh_token = config_file.get(ATTR_REFRESH_TOKEN) # This will be called after authorizing Home-Assistant if None not in (access_token, refresh_token): pywink.set_wink_credentials(config_file.get(ATTR_CLIENT_ID), config_file.get(ATTR_CLIENT_SECRET), access_token=access_token, refresh_token=refresh_token) # This is called to create the redirect so the user can Authorize # Home-Assistant else: redirect_uri = '{}{}'.format(hass.config.api.base_url, WINK_AUTH_CALLBACK_PATH) wink_auth_start_url = pywink.get_authorization_url( config_file.get(ATTR_CLIENT_ID), redirect_uri) hass.http.register_redirect(WINK_AUTH_START, wink_auth_start_url) hass.http.register_view(WinkAuthCallbackView(config, config_file, pywink.request_token)) _request_oauth_completion(hass, config) return True pywink.set_user_agent(USER_AGENT) hass.data[DOMAIN]['pubnub'] = PubNubSubscriptionHandler( pywink.get_subscription_key()) def _subscribe(): hass.data[DOMAIN]['pubnub'].subscribe() # Call subscribe after the user sets up wink via the configurator # All other methods will complete setup before # EVENT_HOMEASSISTANT_START is called meaning they # will call subscribe via the method below. (start_subscription) if hass.data[DOMAIN]['configurator']: _subscribe() def keep_alive_call(event_time): """Call the Wink API endpoints to keep PubNub working.""" _LOGGER.info("Polling the Wink API to keep PubNub updates flowing.") pywink.set_user_agent(str(int(time.time()))) _temp_response = pywink.get_user() _LOGGER.debug(str(json.dumps(_temp_response))) time.sleep(1) pywink.set_user_agent(USER_AGENT) _temp_response = pywink.wink_api_fetch() _LOGGER.debug(str(json.dumps(_temp_response))) # Call the Wink API every hour to keep PubNub updates flowing track_time_interval(hass, keep_alive_call, timedelta(minutes=60)) def start_subscription(event): """Start the pubnub subscription.""" _subscribe() hass.bus.listen(EVENT_HOMEASSISTANT_START, start_subscription) def stop_subscription(event): """Stop the pubnub subscription.""" hass.data[DOMAIN]['pubnub'].unsubscribe() hass.data[DOMAIN]['pubnub'] = None hass.bus.listen(EVENT_HOMEASSISTANT_STOP, stop_subscription) def save_credentials(event): """Save currently set oauth credentials.""" if hass.data[DOMAIN]["oauth"].get("email") is None: config_path = hass.config.path(WINK_CONFIG_FILE) _config = pywink.get_current_oauth_credentials() save_json(config_path, _config) hass.bus.listen(EVENT_HOMEASSISTANT_STOP, save_credentials) def force_update(call): """Force all devices to poll the Wink API.""" _LOGGER.info("Refreshing Wink states from API") for entity_list in hass.data[DOMAIN]['entities'].values(): # Throttle the calls to Wink API for entity in entity_list: time.sleep(1) entity.schedule_update_ha_state(True) hass.services.register(DOMAIN, SERVICE_REFRESH_STATES, force_update, descriptions.get(SERVICE_REFRESH_STATES)) def pull_new_devices(call): """Pull new devices added to users Wink account since startup.""" _LOGGER.info("Getting new devices from Wink API") for _component in WINK_COMPONENTS: discovery.load_platform(hass, _component, DOMAIN, {}, config) hass.services.register(DOMAIN, SERVICE_ADD_NEW_DEVICES, pull_new_devices, descriptions.get(SERVICE_ADD_NEW_DEVICES)) def set_pairing_mode(call): """Put the hub in provided pairing mode.""" hub_name = call.data.get('hub_name') pairing_mode = call.data.get('pairing_mode') kidde_code = call.data.get('kidde_radio_code') for hub in WINK_HUBS: if hub.name() == hub_name: hub.pair_new_device(pairing_mode, kidde_radio_code=kidde_code) def rename_device(call): """Set specified device's name.""" # This should only be called on one device at a time. found_device = None entity_id = call.data.get('entity_id')[0] all_devices = [] for list_of_devices in hass.data[DOMAIN]['entities'].values(): all_devices += list_of_devices for device in all_devices: if device.entity_id == entity_id: found_device = device if found_device is not None: name = call.data.get('name') found_device.wink.set_name(name) hass.services.register(DOMAIN, SERVICE_RENAME_DEVICE, rename_device, descriptions.get(SERVICE_RENAME_DEVICE), schema=RENAME_DEVICE_SCHEMA) def delete_device(call): """Delete specified device.""" # This should only be called on one device at a time. found_device = None entity_id = call.data.get('entity_id')[0] all_devices = [] for list_of_devices in hass.data[DOMAIN]['entities'].values(): all_devices += list_of_devices for device in all_devices: if device.entity_id == entity_id: found_device = device if found_device is not None: found_device.wink.remove_device() hass.services.register(DOMAIN, SERVICE_DELETE_DEVICE, delete_device, descriptions.get(SERVICE_DELETE_DEVICE), schema=DELETE_DEVICE_SCHEMA) hubs = pywink.get_hubs() for hub in hubs: if hub.device_manufacturer() == 'wink': WINK_HUBS.append(hub) if WINK_HUBS: hass.services.register( DOMAIN, SERVICE_SET_PAIRING_MODE, set_pairing_mode, descriptions.get(SERVICE_SET_PAIRING_MODE), schema=SET_PAIRING_MODE_SCHEMA) def service_handle(service): """Handler for services.""" entity_ids = service.data.get('entity_id') all_sirens = [] for switch in hass.data[DOMAIN]['entities']['switch']: if isinstance(switch, WinkSirenDevice): all_sirens.append(switch) sirens_to_set = [] if entity_ids is None: sirens_to_set = all_sirens else: for siren in all_sirens: if siren.entity_id in entity_ids: sirens_to_set.append(siren) for siren in sirens_to_set: if (service.service != SERVICE_SET_AUTO_SHUTOFF and service.service != SERVICE_ENABLE_SIREN and siren.wink.device_manufacturer() != 'dome'): _LOGGER.error("Service only valid for Dome sirens.") return if service.service == SERVICE_ENABLE_SIREN: siren.wink.set_state(service.data.get(ATTR_ENABLED)) elif service.service == SERVICE_SET_AUTO_SHUTOFF: siren.wink.set_auto_shutoff( service.data.get(ATTR_AUTO_SHUTOFF)) elif service.service == SERVICE_SET_CHIME_VOLUME: siren.wink.set_chime_volume(service.data.get(ATTR_VOLUME)) elif service.service == SERVICE_SET_SIREN_VOLUME: siren.wink.set_siren_volume(service.data.get(ATTR_VOLUME)) elif service.service == SERVICE_SET_SIREN_TONE: siren.wink.set_siren_sound(service.data.get(ATTR_TONE)) elif service.service == SERVICE_ENABLE_CHIME: siren.wink.set_chime(service.data.get(ATTR_TONE)) elif service.service == SERVICE_SIREN_STROBE_ENABLED: siren.wink.set_siren_strobe_enabled( service.data.get(ATTR_ENABLED)) elif service.service == SERVICE_CHIME_STROBE_ENABLED: siren.wink.set_chime_strobe_enabled( service.data.get(ATTR_ENABLED)) # Load components for the devices in Wink that we support for wink_component in WINK_COMPONENTS: hass.data[DOMAIN]['entities'][wink_component] = [] discovery.load_platform(hass, wink_component, DOMAIN, {}, config) component = EntityComponent(_LOGGER, DOMAIN, hass) sirens = [] has_dome_siren = False for siren in pywink.get_sirens(): if siren.device_manufacturer() == "dome": has_dome_siren = True _id = siren.object_id() + siren.name() if _id not in hass.data[DOMAIN]['unique_ids']: sirens.append(WinkSirenDevice(siren, hass)) if sirens: hass.services.register(DOMAIN, SERVICE_SET_AUTO_SHUTOFF, service_handle, descriptions.get(SERVICE_SET_AUTO_SHUTOFF), schema=SET_AUTO_SHUTOFF_SCHEMA) hass.services.register(DOMAIN, SERVICE_ENABLE_SIREN, service_handle, descriptions.get(SERVICE_ENABLE_SIREN), schema=ENABLED_SIREN_SCHEMA) if has_dome_siren: hass.services.register(DOMAIN, SERVICE_SET_SIREN_TONE, service_handle, descriptions.get(SERVICE_SET_SIREN_TONE), schema=SET_SIREN_TONE_SCHEMA) hass.services.register(DOMAIN, SERVICE_ENABLE_CHIME, service_handle, descriptions.get(SERVICE_ENABLE_CHIME), schema=SET_CHIME_MODE_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_SIREN_VOLUME, service_handle, descriptions.get(SERVICE_SET_SIREN_VOLUME), schema=SET_VOLUME_SCHEMA) hass.services.register(DOMAIN, SERVICE_SET_CHIME_VOLUME, service_handle, descriptions.get(SERVICE_SET_CHIME_VOLUME), schema=SET_VOLUME_SCHEMA) hass.services.register(DOMAIN, SERVICE_SIREN_STROBE_ENABLED, service_handle, descriptions.get(SERVICE_SIREN_STROBE_ENABLED), schema=SET_STROBE_ENABLED_SCHEMA) hass.services.register(DOMAIN, SERVICE_CHIME_STROBE_ENABLED, service_handle, descriptions.get(SERVICE_CHIME_STROBE_ENABLED), schema=SET_STROBE_ENABLED_SCHEMA) component.add_entities(sirens) return True
def test_duplicate_key(caplog): """Test duplicate dict keys.""" files = {YAML_CONFIG_FILE: 'key: thing1\nkey: thing2'} with patch_yaml_files(files): load_yaml_config_file(YAML_CONFIG_FILE) assert 'contains duplicate key' in caplog.text
def setup(hass: HomeAssistantType, config: ConfigType): """Setup device tracker.""" yaml_path = hass.config.path(YAML_DEVICES) try: conf = _CONFIG_SCHEMA(config).get(DOMAIN, []) except vol.Invalid as ex: log_exception(ex, DOMAIN, config) return False else: conf = conf[0] if len(conf) > 0 else {} consider_home = conf.get(CONF_CONSIDER_HOME, timedelta(seconds=DEFAULT_CONSIDER_HOME)) track_new = conf.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW) devices = load_config(yaml_path, hass, consider_home) tracker = DeviceTracker(hass, consider_home, track_new, devices) def setup_platform(p_type, p_config, disc_info=None): """Setup a device tracker platform.""" platform = prepare_setup_platform(hass, config, DOMAIN, p_type) if platform is None: return try: if hasattr(platform, 'get_scanner'): scanner = platform.get_scanner(hass, {DOMAIN: p_config}) if scanner is None: _LOGGER.error('Error setting up platform %s', p_type) return setup_scanner_platform(hass, p_config, scanner, tracker.see) return if not platform.setup_scanner(hass, p_config, tracker.see): _LOGGER.error('Error setting up platform %s', p_type) except Exception: # pylint: disable=broad-except _LOGGER.exception('Error setting up platform %s', p_type) for p_type, p_config in config_per_platform(config, DOMAIN): setup_platform(p_type, p_config) def device_tracker_discovered(service, info): """Called when a device tracker platform is discovered.""" setup_platform(DISCOVERY_PLATFORMS[service], {}, info) discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), device_tracker_discovered) def update_stale(now): """Clean up stale devices.""" tracker.update_stale(now) track_utc_time_change(hass, update_stale, second=range(0, 60, 5)) tracker.setup_group() def see_service(call): """Service to see a device.""" args = { key: value for key, value in call.data.items() if key in (ATTR_MAC, ATTR_DEV_ID, ATTR_HOST_NAME, ATTR_LOCATION_NAME, ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_BATTERY, ATTR_ATTRIBUTES) } tracker.see(**args) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_SEE, see_service, descriptions.get(SERVICE_SEE)) return True
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Sonos platform.""" import soco if discovery_info: player = soco.SoCo(discovery_info) if player.is_visible: add_devices([SonosDevice(hass, player)]) return True return False players = None hosts = config.get('hosts', None) if hosts: # Support retro compatibility with comma separated list of hosts # from config hosts = hosts.split(',') if isinstance(hosts, str) else hosts players = [] for host in hosts: players.append(soco.SoCo(socket.gethostbyname(host))) if not players: players = soco.discover( interface_addr=config.get('interface_addr', None)) if not players: _LOGGER.warning('No Sonos speakers found.') return False devices = [SonosDevice(hass, p) for p in players] add_devices(devices) _LOGGER.info('Added %s Sonos speakers', len(players)) def _apply_service(service, service_func, *service_func_args): """Internal func for applying a service.""" entity_id = service.data.get('entity_id') if entity_id: _devices = [ device for device in devices if device.entity_id == entity_id ] else: _devices = devices for device in _devices: service_func(device, *service_func_args) device.update_ha_state(True) def group_players_service(service): """Group media players, use player as coordinator.""" _apply_service(service, SonosDevice.group_players) def unjoin_service(service): """Unjoin the player from a group.""" _apply_service(service, SonosDevice.unjoin) def snapshot_service(service): """Take a snapshot.""" _apply_service(service, SonosDevice.snapshot) def restore_service(service): """Restore a snapshot.""" _apply_service(service, SonosDevice.restore) descriptions = load_yaml_config_file( path.join(path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_GROUP_PLAYERS, group_players_service, descriptions.get(SERVICE_GROUP_PLAYERS)) hass.services.register(DOMAIN, SERVICE_UNJOIN, unjoin_service, descriptions.get(SERVICE_UNJOIN)) hass.services.register(DOMAIN, SERVICE_SNAPSHOT, snapshot_service, descriptions.get(SERVICE_SNAPSHOT)) hass.services.register(DOMAIN, SERVICE_RESTORE, restore_service, descriptions.get(SERVICE_RESTORE)) return True
def setup_bridge(host, hass, add_devices, filename, allow_unreachable, allow_in_emulated_hue, allow_hue_groups): """Set up a phue bridge based on host parameter.""" import phue try: bridge = phue.Bridge(host, config_file_path=hass.config.path(filename)) except ConnectionRefusedError: # Wrong host was given _LOGGER.error("Error connecting to the Hue bridge at %s", host) return except phue.PhueRegistrationException: _LOGGER.warning("Connected to Hue at %s but not registered.", host) request_configuration(host, hass, add_devices, filename, allow_unreachable, allow_in_emulated_hue, allow_hue_groups) return # If we came here and configuring this host, mark as done if host in _CONFIGURING: request_id = _CONFIGURING.pop(host) configurator = get_component('configurator') configurator.request_done(request_id) lights = {} lightgroups = {} skip_groups = not allow_hue_groups @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_lights(): """Update the Hue light objects with latest info from the bridge.""" nonlocal skip_groups try: api = bridge.get_api() except phue.PhueRequestTimeout: _LOGGER.warning("Timeout trying to reach the bridge") return except ConnectionRefusedError: _LOGGER.error("The bridge refused the connection") return except socket.error: # socket.error when we cannot reach Hue _LOGGER.exception("Cannot reach the bridge") return api_lights = api.get('lights') if not isinstance(api_lights, dict): _LOGGER.error("Got unexpected result from Hue API") return if skip_groups: api_groups = {} else: api_groups = api.get('groups') if not isinstance(api_groups, dict): _LOGGER.error("Got unexpected result from Hue API") return new_lights = [] api_name = api.get('config').get('name') if api_name in ('RaspBee-GW', 'deCONZ-GW'): bridge_type = 'deconz' else: bridge_type = 'hue' for light_id, info in api_lights.items(): if light_id not in lights: lights[light_id] = HueLight(int(light_id), info, bridge, update_lights, bridge_type, allow_unreachable, allow_in_emulated_hue) new_lights.append(lights[light_id]) else: lights[light_id].info = info lights[light_id].schedule_update_ha_state() for lightgroup_id, info in api_groups.items(): if 'state' not in info: _LOGGER.warning("Group info does not contain state. " "Please update your hub.") skip_groups = True break if lightgroup_id not in lightgroups: lightgroups[lightgroup_id] = HueLight(int(lightgroup_id), info, bridge, update_lights, bridge_type, allow_unreachable, allow_in_emulated_hue, True) new_lights.append(lightgroups[lightgroup_id]) else: lightgroups[lightgroup_id].info = info lightgroups[lightgroup_id].schedule_update_ha_state() if new_lights: add_devices(new_lights) _CONFIGURED_BRIDGES[socket.gethostbyname(host)] = True # create a service for calling run_scene directly on the bridge, # used to simplify automation rules. def hue_activate_scene(call): """Service to call directly directly into bridge to set scenes.""" group_name = call.data[ATTR_GROUP_NAME] scene_name = call.data[ATTR_SCENE_NAME] bridge.run_scene(group_name, scene_name) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_HUE_SCENE, hue_activate_scene, descriptions.get(SERVICE_HUE_SCENE), schema=SCENE_SCHEMA) update_lights()
def setup(hass, config): """Set up Z-Wave. Will automatically load components to support devices found on the network. """ descriptions = conf_util.load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) from pydispatch import dispatcher # pylint: disable=import-error from openzwave.option import ZWaveOption from openzwave.network import ZWaveNetwork from openzwave.group import ZWaveGroup # Load configuration use_debug = config[DOMAIN].get(CONF_DEBUG) autoheal = config[DOMAIN].get(CONF_AUTOHEAL) device_config = EntityValues(config[DOMAIN][CONF_DEVICE_CONFIG], config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN], config[DOMAIN][CONF_DEVICE_CONFIG_GLOB]) new_entity_ids = config[DOMAIN][CONF_NEW_ENTITY_IDS] if not new_entity_ids: _LOGGER.warning( "ZWave entity_ids will soon be changing. To opt in to new " "entity_ids now, set `new_entity_ids: true` under zwave in your " "configuration.yaml. See the following blog post for details: " "https://home-assistant.io/blog/2017/06/15/zwave-entity-ids/") # Setup options options = ZWaveOption(config[DOMAIN].get(CONF_USB_STICK_PATH), user_path=hass.config.config_dir, config_path=config[DOMAIN].get(CONF_CONFIG_PATH)) options.set_console_output(use_debug) if CONF_NETWORK_KEY in config[DOMAIN]: options.addOption("NetworkKey", config[DOMAIN][CONF_NETWORK_KEY]) options.lock() network = hass.data[DATA_NETWORK] = ZWaveNetwork(options, autostart=False) hass.data[DATA_DEVICES] = {} hass.data[DATA_ENTITY_VALUES] = [] if use_debug: # pragma: no cover def log_all(signal, value=None): """Log all the signals.""" print("") print("SIGNAL *****", signal) if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED, ZWaveNetwork.SIGNAL_VALUE_ADDED, ZWaveNetwork.SIGNAL_SCENE_EVENT, ZWaveNetwork.SIGNAL_NODE_EVENT, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED): pprint(_obj_to_dict(value)) print("") dispatcher.connect(log_all, weak=False) def value_added(node, value): """Handle new added value to a node on the network.""" # Check if this value should be tracked by an existing entity for values in hass.data[DATA_ENTITY_VALUES]: values.check_value(value) for schema in DISCOVERY_SCHEMAS: if not check_node_schema(node, schema): continue if not check_value_schema( value, schema[const.DISC_VALUES][const.DISC_PRIMARY]): continue values = ZWaveDeviceEntityValues(hass, schema, value, config, device_config) # We create a new list and update the reference here so that # the list can be safely iterated over in the main thread new_values = hass.data[DATA_ENTITY_VALUES] + [values] hass.data[DATA_ENTITY_VALUES] = new_values component = EntityComponent(_LOGGER, DOMAIN, hass) def node_added(node): """Handle a new node on the network.""" entity = ZWaveNodeEntity(node, network, new_entity_ids) name = node_name(node) if new_entity_ids: generated_id = generate_entity_id(DOMAIN + '.{}', name, []) else: generated_id = entity.entity_id node_config = device_config.get(generated_id) if node_config.get(CONF_IGNORED): _LOGGER.info("Ignoring node entity %s due to device settings", generated_id) return component.add_entities([entity]) def network_ready(): """Handle the query of all awake nodes.""" _LOGGER.info("Zwave network is ready for use. All awake nodes " "have been queried. Sleeping nodes will be " "queried when they awake.") hass.bus.fire(const.EVENT_NETWORK_READY) def network_complete(): """Handle the querying of all nodes on network.""" _LOGGER.info("Z-Wave network is complete. All nodes on the network " "have been queried") hass.bus.fire(const.EVENT_NETWORK_COMPLETE) dispatcher.connect(value_added, ZWaveNetwork.SIGNAL_VALUE_ADDED, weak=False) dispatcher.connect(node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False) dispatcher.connect(network_ready, ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED, weak=False) dispatcher.connect(network_complete, ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED, weak=False) def add_node(service): """Switch into inclusion mode.""" _LOGGER.info("Z-Wave add_node have been initialized") network.controller.add_node() def add_node_secure(service): """Switch into secure inclusion mode.""" _LOGGER.info("Z-Wave add_node_secure have been initialized") network.controller.add_node(True) def remove_node(service): """Switch into exclusion mode.""" _LOGGER.info("Z-Wwave remove_node have been initialized") network.controller.remove_node() def cancel_command(service): """Cancel a running controller command.""" _LOGGER.info("Cancel running Z-Wave command") network.controller.cancel_command() def heal_network(service): """Heal the network.""" _LOGGER.info("Z-Wave heal running") network.heal() def soft_reset(service): """Soft reset the controller.""" _LOGGER.info("Z-Wave soft_reset have been initialized") network.controller.soft_reset() def test_network(service): """Test the network by sending commands to all the nodes.""" _LOGGER.info("Z-Wave test_network have been initialized") network.test() def stop_network(_service_or_event): """Stop Z-Wave network.""" _LOGGER.info("Stopping Z-Wave network") network.stop() if hass.state == CoreState.running: hass.bus.fire(const.EVENT_NETWORK_STOP) def rename_node(service): """Rename a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] name = service.data.get(const.ATTR_NAME) node.name = name _LOGGER.info("Renamed Z-Wave node %d to %s", node_id, name) def rename_value(service): """Rename a node value.""" node_id = service.data.get(const.ATTR_NODE_ID) value_id = service.data.get(const.ATTR_VALUE_ID) node = network.nodes[node_id] value = node.values[value_id] name = service.data.get(const.ATTR_NAME) value.label = name _LOGGER.info("Renamed Z-Wave value (Node %d Value %d) to %s", node_id, value_id, name) def set_poll_intensity(service): """Set the polling intensity of a node value.""" node_id = service.data.get(const.ATTR_NODE_ID) value_id = service.data.get(const.ATTR_VALUE_ID) node = network.nodes[node_id] value = node.values[value_id] intensity = service.data.get(const.ATTR_POLL_INTENSITY) if intensity == 0: if value.disable_poll(): _LOGGER.info("Polling disabled (Node %d Value %d)", node_id, value_id) return _LOGGER.info("Polling disabled failed (Node %d Value %d)", node_id, value_id) else: if value.enable_poll(intensity): _LOGGER.info("Set polling intensity (Node %d Value %d) to %s", node_id, value_id, intensity) return _LOGGER.info("Set polling intensity failed (Node %d Value %d)", node_id, value_id) def remove_failed_node(service): """Remove failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) _LOGGER.info("Trying to remove zwave node %d", node_id) network.controller.remove_failed_node(node_id) def replace_failed_node(service): """Replace failed node.""" node_id = service.data.get(const.ATTR_NODE_ID) _LOGGER.info("Trying to replace zwave node %d", node_id) network.controller.replace_failed_node(node_id) def set_config_parameter(service): """Set a config parameter to a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) selection = service.data.get(const.ATTR_CONFIG_VALUE) size = service.data.get(const.ATTR_CONFIG_SIZE) for value in (node.get_values( class_id=const.COMMAND_CLASS_CONFIGURATION).values()): if value.index != param: continue if value.type in [const.TYPE_LIST, const.TYPE_BOOL]: value.data = selection _LOGGER.info( "Setting config list parameter %s on Node %s " "with selection %s", param, node_id, selection) return value.data = int(selection) _LOGGER.info( "Setting config parameter %s on Node %s " "with selection %s", param, node_id, selection) return node.set_config_param(param, selection, size) _LOGGER.info( "Setting unknown config parameter %s on Node %s " "with selection %s", param, node_id, selection) def print_config_parameter(service): """Print a config parameter from a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] param = service.data.get(const.ATTR_CONFIG_PARAMETER) _LOGGER.info("Config parameter %s on Node %s: %s", param, node_id, get_config_value(node, param)) def print_node(service): """Print all information about z-wave node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] nice_print_node(node) def set_wakeup(service): """Set wake-up interval of a node.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] value = service.data.get(const.ATTR_CONFIG_VALUE) if node.can_wake_up(): for value_id in node.get_values( class_id=const.COMMAND_CLASS_WAKE_UP): node.values[value_id].data = value _LOGGER.info("Node %s wake-up set to %d", node_id, value) else: _LOGGER.info("Node %s is not wakeable", node_id) def change_association(service): """Change an association in the zwave network.""" association_type = service.data.get(const.ATTR_ASSOCIATION) node_id = service.data.get(const.ATTR_NODE_ID) target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID) group = service.data.get(const.ATTR_GROUP) instance = service.data.get(const.ATTR_INSTANCE) node = ZWaveGroup(group, network, node_id) if association_type == 'add': node.add_association(target_node_id, instance) _LOGGER.info( "Adding association for node:%s in group:%s " "target node:%s, instance=%s", node_id, group, target_node_id, instance) if association_type == 'remove': node.remove_association(target_node_id, instance) _LOGGER.info( "Removing association for node:%s in group:%s " "target node:%s, instance=%s", node_id, group, target_node_id, instance) @asyncio.coroutine def async_refresh_entity(service): """Refresh values that specific entity depends on.""" entity_id = service.data.get(ATTR_ENTITY_ID) async_dispatcher_send(hass, SIGNAL_REFRESH_ENTITY_FORMAT.format(entity_id)) def refresh_node(service): """Refresh all node info.""" node_id = service.data.get(const.ATTR_NODE_ID) node = network.nodes[node_id] node.refresh_info() def reset_node_meters(service): """Reset meter counters of a node.""" node_id = service.data.get(const.ATTR_NODE_ID) instance = service.data.get(const.ATTR_INSTANCE) node = network.nodes[node_id] for value in (node.get_values( class_id=const.COMMAND_CLASS_METER).values()): if value.index != const.INDEX_METER_RESET: continue if value.instance != instance: continue network.manager.pressButton(value.value_id) network.manager.releaseButton(value.value_id) _LOGGER.info("Resetting meters on node %s instance %s....", node_id, instance) return _LOGGER.info( "Node %s on instance %s does not have resettable " "meters.", node_id, instance) def start_zwave(_service_or_event): """Startup Z-Wave network.""" _LOGGER.info("Starting Z-Wave network...") network.start() hass.bus.fire(const.EVENT_NETWORK_START) # Need to be in STATE_AWAKED before talking to nodes. # Wait up to NETWORK_READY_WAIT_SECS seconds for the zwave network # to be ready. for i in range(const.NETWORK_READY_WAIT_SECS): _LOGGER.debug("network state: %d %s", network.state, network.state_str) if network.state >= network.STATE_AWAKED: _LOGGER.info("Z-Wave ready after %d seconds", i) break time.sleep(1) else: _LOGGER.warning( "zwave not ready after %d seconds, continuing anyway", const.NETWORK_READY_WAIT_SECS) _LOGGER.info("final network state: %d %s", network.state, network.state_str) polling_interval = convert(config[DOMAIN].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) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_network) # Register node services for Z-Wave network hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node, descriptions[const.SERVICE_ADD_NODE]) hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE, add_node_secure, descriptions[const.SERVICE_ADD_NODE_SECURE]) hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node, descriptions[const.SERVICE_REMOVE_NODE]) hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND, cancel_command, descriptions[const.SERVICE_CANCEL_COMMAND]) hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK, heal_network, descriptions[const.SERVICE_HEAL_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset, descriptions[const.SERVICE_SOFT_RESET]) hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK, test_network, descriptions[const.SERVICE_TEST_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK, stop_network, descriptions[const.SERVICE_STOP_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_START_NETWORK, start_zwave, descriptions[const.SERVICE_START_NETWORK]) hass.services.register(DOMAIN, const.SERVICE_RENAME_NODE, rename_node, descriptions[const.SERVICE_RENAME_NODE], schema=RENAME_NODE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_RENAME_VALUE, rename_value, descriptions[const.SERVICE_RENAME_VALUE], schema=RENAME_VALUE_SCHEMA) hass.services.register( DOMAIN, const.SERVICE_SET_CONFIG_PARAMETER, set_config_parameter, descriptions[const.SERVICE_SET_CONFIG_PARAMETER], schema=SET_CONFIG_PARAMETER_SCHEMA) hass.services.register( DOMAIN, const.SERVICE_PRINT_CONFIG_PARAMETER, print_config_parameter, descriptions[const.SERVICE_PRINT_CONFIG_PARAMETER], schema=PRINT_CONFIG_PARAMETER_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REMOVE_FAILED_NODE, remove_failed_node, descriptions[const.SERVICE_REMOVE_FAILED_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REPLACE_FAILED_NODE, replace_failed_node, descriptions[const.SERVICE_REPLACE_FAILED_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_CHANGE_ASSOCIATION, change_association, descriptions[const.SERVICE_CHANGE_ASSOCIATION], schema=CHANGE_ASSOCIATION_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_SET_WAKEUP, set_wakeup, descriptions[const.SERVICE_SET_WAKEUP], schema=SET_WAKEUP_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_PRINT_NODE, print_node, descriptions[const.SERVICE_PRINT_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REFRESH_ENTITY, async_refresh_entity, descriptions[const.SERVICE_REFRESH_ENTITY], schema=REFRESH_ENTITY_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_REFRESH_NODE, refresh_node, descriptions[const.SERVICE_REFRESH_NODE], schema=NODE_SERVICE_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_RESET_NODE_METERS, reset_node_meters, descriptions[const.SERVICE_RESET_NODE_METERS], schema=RESET_NODE_METERS_SCHEMA) hass.services.register(DOMAIN, const.SERVICE_SET_POLL_INTENSITY, set_poll_intensity, descriptions[const.SERVICE_SET_POLL_INTENSITY], schema=SET_POLL_INTENSITY_SCHEMA) # Setup autoheal if autoheal: _LOGGER.info("Z-Wave network autoheal is enabled") track_time_change(hass, heal_network, hour=0, minute=0, second=0) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave) return True
def load_yaml(fname, string): """Write a string to file and return the parsed yaml.""" FILES[fname] = string with patch_yaml_files(FILES): return load_yaml_config_file(fname)
def setup(hass, config): """ Track states and offer events for media_players. """ component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS) component.setup(config) descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) def media_player_service_handler(service): """ Maps services to methods on MediaPlayerDevice. """ target_players = component.extract_from_service(service) method = SERVICE_TO_METHOD[service.service] for player in target_players: getattr(player, method)() if player.should_poll: player.update_ha_state(True) for service in SERVICE_TO_METHOD: hass.services.register(DOMAIN, service, media_player_service_handler, descriptions.get(service)) def volume_set_service(service): """ Set specified volume on the media player. """ target_players = component.extract_from_service(service) if ATTR_MEDIA_VOLUME_LEVEL not in service.data: return volume = service.data[ATTR_MEDIA_VOLUME_LEVEL] for player in target_players: player.set_volume_level(volume) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service, descriptions.get(SERVICE_VOLUME_SET)) def volume_mute_service(service): """ Mute (true) or unmute (false) the media player. """ target_players = component.extract_from_service(service) if ATTR_MEDIA_VOLUME_MUTED not in service.data: return mute = service.data[ATTR_MEDIA_VOLUME_MUTED] for player in target_players: player.mute_volume(mute) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service, descriptions.get(SERVICE_VOLUME_MUTE)) def media_seek_service(service): """ Seek to a position. """ target_players = component.extract_from_service(service) if ATTR_MEDIA_SEEK_POSITION not in service.data: return position = service.data[ATTR_MEDIA_SEEK_POSITION] for player in target_players: player.seek(position) if player.should_poll: player.update_ha_state(True) hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service, descriptions.get(SERVICE_MEDIA_SEEK)) def play_youtube_video_service(service, media_id=None): """ Plays specified media_id on the media player. """ if media_id is None: service.data.get('video') if media_id is None: return for player in component.extract_from_service(service): player.play_youtube(media_id) if player.should_poll: player.update_ha_state(True) def play_media_service(service): """ Plays specified media_id on the media player. """ media_type = service.data.get('media_type') media_id = service.data.get('media_id') if media_type is None: return if media_id is None: return for player in component.extract_from_service(service): player.play_media(media_type, media_id) if player.should_poll: player.update_ha_state(True) hass.services.register( DOMAIN, "start_fireplace", lambda service: play_youtube_video_service(service, "eyU3bRy2x44"), descriptions.get('start_fireplace')) hass.services.register( DOMAIN, "start_epic_sax", lambda service: play_youtube_video_service(service, "kxopViU98Xo"), descriptions.get('start_epic_sax')) hass.services.register( DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service, descriptions.get(SERVICE_YOUTUBE_VIDEO)) hass.services.register( DOMAIN, SERVICE_PLAY_MEDIA, play_media_service, descriptions.get(SERVICE_PLAY_MEDIA)) return True
def test_unhashable_key(self): """Test an unhasable key.""" files = {YAML_CONFIG_FILE: 'message:\n {{ states.state }}'} with self.assertRaises(HomeAssistantError), \ patch_yaml_files(files): load_yaml_config_file(YAML_CONFIG_FILE)
def setup(hass, config): """ Exposes light control via statemachine and services. """ component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, GROUP_NAME_ALL_LIGHTS) component.setup(config) # Load built-in profiles and custom profiles profile_paths = [ os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE), hass.config.path(LIGHT_PROFILES_FILE) ] profiles = {} for profile_path in profile_paths: if not os.path.isfile(profile_path): continue with open(profile_path) as inp: reader = csv.reader(inp) # Skip the header next(reader, None) try: for profile_id, color_x, color_y, brightness in reader: profiles[profile_id] = (float(color_x), float(color_y), int(brightness)) except ValueError: # ValueError if not 4 values per row # ValueError if convert to float/int failed _LOGGER.error("Error parsing light profiles from %s", profile_path) return False def handle_light_service(service): """ Hande a turn light on or off service call. """ # Get and validate data dat = service.data # Convert the entity ids to valid light ids target_lights = component.extract_from_service(service) params = {} transition = util.convert(dat.get(ATTR_TRANSITION), int) if transition is not None: params[ATTR_TRANSITION] = transition service_fun = None if service.service == SERVICE_TURN_OFF: service_fun = 'turn_off' elif service.service == SERVICE_TOGGLE: service_fun = 'toggle' if service_fun: for light in target_lights: getattr(light, service_fun)(**params) for light in target_lights: if light.should_poll: light.update_ha_state(True) return # Processing extra data for turn light on request # We process the profile first so that we get the desired # behavior that extra service data attributes overwrite # profile values profile = profiles.get(dat.get(ATTR_PROFILE)) if profile: *params[ATTR_XY_COLOR], params[ATTR_BRIGHTNESS] = profile if ATTR_BRIGHTNESS in dat: # We pass in the old value as the default parameter if parsing # of the new one goes wrong. params[ATTR_BRIGHTNESS] = util.convert(dat.get(ATTR_BRIGHTNESS), int, params.get(ATTR_BRIGHTNESS)) if ATTR_XY_COLOR in dat: try: # xy_color should be a list containing 2 floats xycolor = dat.get(ATTR_XY_COLOR) # Without this check, a xycolor with value '99' would work if not isinstance(xycolor, str): params[ATTR_XY_COLOR] = [float(val) for val in xycolor] except (TypeError, ValueError): # TypeError if xy_color is not iterable # ValueError if value could not be converted to float pass if ATTR_COLOR_TEMP in dat: # color_temp should be an int of mirads value colortemp = dat.get(ATTR_COLOR_TEMP) # Without this check, a ctcolor with value '99' would work # These values are based on Philips Hue, may need ajustment later if isinstance(colortemp, int) and 154 <= colortemp <= 500: params[ATTR_COLOR_TEMP] = colortemp if ATTR_RGB_COLOR in dat: try: # rgb_color should be a list containing 3 ints rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: params[ATTR_RGB_COLOR] = [int(val) for val in rgb_color] except (TypeError, ValueError): # TypeError if rgb_color is not iterable # ValueError if not all values can be converted to int pass if dat.get(ATTR_FLASH) in (FLASH_SHORT, FLASH_LONG): params[ATTR_FLASH] = dat[ATTR_FLASH] if dat.get(ATTR_EFFECT) in (EFFECT_COLORLOOP, EFFECT_WHITE, EFFECT_RANDOM): params[ATTR_EFFECT] = dat[ATTR_EFFECT] for light in target_lights: light.turn_on(**params) for light in target_lights: if light.should_poll: light.update_ha_state(True) # Listen for light on and light off service calls descriptions = load_yaml_config_file( os.path.join(os.path.dirname(__file__), 'services.yaml')) hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service, descriptions.get(SERVICE_TURN_ON)) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service, descriptions.get(SERVICE_TURN_OFF)) hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_light_service, descriptions.get(SERVICE_TOGGLE)) return True