def test_websocket_error(fake_home: Home, home_data): global ws_error_called def on_error(err): global ws_error_called ws_error_called = True fake_home.onWsError += on_error fake_home.enable_events() with no_ssl_verification(): fake_home._connection._restCallRequestCounter = 1 fake_home._restCall("ws/sleep", json.dumps({"seconds": 5})) assert ws_error_called # testing automatic reconnection time.sleep(5) # give the reconnection routine time to reconnect client_base_id = "00000000-0000-0000-0000-000000000000" client_changed = home_data["clients"][client_base_id].copy() client_changed["label"] = "CHANGED" send_event(fake_home, EventType.CLIENT_CHANGED, "client", client_changed) time.sleep(1) d = fake_home.search_client_by_id(client_base_id) assert d.label == "CHANGED" assert isinstance(d, Client) fake_home.disable_events()
def test_websocket_client(fake_home: Home, home_data): fake_home.enable_events() # preparing event data for client added client_base_id = "00000000-0000-0000-0000-000000000000" client_added = home_data["clients"][client_base_id].copy() client_added_id = "NEW_CLIENT" client_added["id"] = client_added_id send_event(fake_home, EventType.CLIENT_ADDED, "client", client_added) # preparing event data for client changed client_changed = home_data["clients"][client_base_id].copy() client_changed["label"] = "CHANGED" send_event(fake_home, EventType.CLIENT_CHANGED, "client", client_changed) # preparing event data for client remove client_delete_id = "AA000000-0000-0000-0000-000000000000" c = fake_home.search_client_by_id(client_delete_id) assert c != None assert c.label == "REMOVE_ME" send_event(fake_home, EventType.CLIENT_REMOVED, "id", client_delete_id) time.sleep(1) d = fake_home.search_client_by_id(client_added_id) assert d.label == "TEST-Client" assert isinstance(d, Client) d = fake_home.search_client_by_id(client_base_id) assert d.label == "CHANGED" assert isinstance(d, Client) assert fake_home.search_client_by_id(client_delete_id) == None fake_home.disable_events()
def test_websocket_home_changed(fake_home: Home, home_data): fake_home.enable_events() new_home = home_data["home"].copy() new_home["weather"]["humidity"] = 60 assert fake_home.weather.humidity == 54 send_event(fake_home, EventType.HOME_CHANGED, "home", new_home) time.sleep(1) assert fake_home.weather.humidity == 60 fake_home.disable_events()
def setup(hass, config): """Set up the HomematicIP component.""" # pylint: disable=import-error, no-name-in-module from homematicip.home import Home hass.data.setdefault(DOMAIN, {}) homes = hass.data[DOMAIN] accesspoints = config.get(DOMAIN, []) def _update_event(events): """Handle incoming HomeMaticIP events.""" for event in events: etype = event['eventType'] edata = event['data'] if etype == 'DEVICE_CHANGED': dispatcher_send(hass, EVENT_DEVICE_CHANGED, edata.id) elif etype == 'GROUP_CHANGED': dispatcher_send(hass, EVENT_GROUP_CHANGED, edata.id) elif etype == 'HOME_CHANGED': dispatcher_send(hass, EVENT_HOME_CHANGED, edata.id) elif etype == 'JOURNAL_CHANGED': dispatcher_send(hass, EVENT_SECURITY_CHANGED, edata.id) return True for device in accesspoints: name = device.get(CONF_NAME) accesspoint = device.get(CONF_ACCESSPOINT) authtoken = device.get(CONF_AUTHTOKEN) home = Home() if name.lower() == 'none': name = '' home.label = name try: home.set_auth_token(authtoken) home.init(accesspoint) if home.get_current_state(): _LOGGER.info("Connection to HMIP established") else: _LOGGER.warning("Connection to HMIP could not be established") return False except timeout: _LOGGER.warning("Connection to HMIP could not be established") return False homes[home.id] = home home.onEvent += _update_event home.enable_events() _LOGGER.info('HUB name: %s, id: %s', home.label, home.id) for component in ['sensor']: load_platform(hass, component, DOMAIN, {'homeid': home.id}, config) return True
def test_websocket_device(fake_home: Home, home_data): fake_home.enable_events() # preparing event data for device added device_base_id = "3014F7110000000000000031" device_added = home_data["devices"][device_base_id].copy() device_added_id = "NEW_DEVICE" device_added["id"] = device_added_id for x in device_added["functionalChannels"].values(): x["deviceId"] = device_added_id send_event(fake_home, EventType.DEVICE_ADDED, "device", device_added) device_added = home_data["devices"][device_base_id].copy() device_added_by_change_id = "NEW_DEVICE_2" device_added["id"] = device_added_by_change_id for x in device_added["functionalChannels"].values(): x["deviceId"] = device_added_by_change_id send_event(fake_home, EventType.DEVICE_CHANGED, "device", device_added) # preparing event data for device changed device_changed = home_data["devices"][device_base_id].copy() device_changed["label"] = "CHANGED" send_event(fake_home, EventType.DEVICE_CHANGED, "device", device_changed) # preparing event data for device remove device_delete_id = "3014F7110000000000BCBB11" assert fake_home.search_device_by_id(device_delete_id) != None send_event(fake_home, EventType.DEVICE_REMOVED, "id", device_delete_id) time.sleep(1) d = fake_home.search_device_by_id(device_added_id) assert d.label == "Garagentor" assert isinstance(d, AccelerationSensor) d = fake_home.search_device_by_id(device_added_by_change_id) assert d.label == "Garagentor" assert isinstance(d, AccelerationSensor) d = fake_home.search_device_by_id(device_base_id) assert d.label == "CHANGED" assert isinstance(d, AccelerationSensor) assert fake_home.search_device_by_id(device_delete_id) == None fake_home.disable_events()
def test_websocket_group(fake_home: Home, home_data): fake_home.enable_events() # preparing event data for group added group_base_id = "00000000-0000-0000-0000-000000000020" group_added = home_data["groups"][group_base_id].copy() group_added_id = "NEW_group" group_added["id"] = group_added_id send_event(fake_home, EventType.GROUP_ADDED, "group", group_added) group_added = home_data["groups"][group_base_id].copy() group_added_by_change_id = "NEW_group_2" group_added["id"] = group_added_by_change_id send_event(fake_home, EventType.GROUP_CHANGED, "group", group_added) # preparing event data for group changed group_changed_id = "00000000-0000-0000-0000-000000000016" group_changed = home_data["groups"][group_changed_id].copy() group_changed["label"] = "CHANGED" send_event(fake_home, EventType.GROUP_CHANGED, "group", group_changed) # preparing event data for group remove group_delete_id = "00000000-0000-0000-0000-000000000012" assert fake_home.search_group_by_id(group_delete_id) != None send_event(fake_home, EventType.GROUP_REMOVED, "id", group_delete_id) time.sleep(1) d = fake_home.search_group_by_id(group_added_id) assert d.label == "Badezimmer" assert isinstance(d, MetaGroup) d = fake_home.search_group_by_id(group_added_by_change_id) assert d.label == "Badezimmer" assert isinstance(d, MetaGroup) d = fake_home.search_group_by_id(group_changed_id) assert d.label == "CHANGED" assert isinstance(d, SecurityZoneGroup) assert fake_home.search_group_by_id(group_delete_id) == None fake_home.disable_events()
def main(): parser = ArgumentParser( description="a cli wrapper for the homematicip API") parser.add_argument( "--config_file", type=str, help= "the configuration file. If nothing is specified the script will search for it.", ) parser.add_argument( "--server-config", type=str, dest="server_config", help= "the server configuration file. e.g. output from --dump-configuration.", ) parser.add_argument( "--debug-level", dest="debug_level", type=int, help="the debug level which should get used(Critical=50, DEBUG=10)", ) parser.add_argument( "--version", action="version", version="%(prog)s {}".format(homematicip.__version__), ) group = parser.add_argument_group("Display Configuration") group.add_argument( "--dump-configuration", action="store_true", dest="dump_config", help="dumps the current configuration from the AP", ) group.add_argument( "--anonymize", action="store_true", dest="anonymize", help="used together with --dump-configuration to anonymize the output", ) group.add_argument( "--list-devices", action="store_true", dest="list_devices", help="list all devices", ) group.add_argument("--list-groups", action="store_true", dest="list_groups", help="list all groups") group.add_argument( "--list-group-ids", action="store_true", dest="list_group_ids", help="list all groups and their ids", ) group.add_argument( "--list-firmware", action="store_true", dest="list_firmware", help="list the firmware of all devices", ) group.add_argument( "--list-rssi", action="store_true", dest="list_rssi", help="list the reception quality of all devices", ) group.add_argument( "--list-events", action="store_true", dest="list_events", help="prints all the events", ) group.add_argument( "--list-last-status-update", action="store_true", dest="list_last_status_update", help="prints the last status update of all systems", ) parser.add_argument( "--list-security-journal", action="store_true", dest="list_security_journal", help="display the security journal", ) parser.add_argument( "--list-rules", action="store_true", dest="list_rules", help="display all automation rules", ) parser.add_argument( "-d", "--device", dest="device", action="append", help= 'the device you want to modify (see "Device Settings").\nYou can use * to modify all devices or enter the parameter multiple times to modify more devices', ) parser.add_argument( "-g", "--group", dest="group", help='the group you want to modify (see "Group Settings")', ) parser.add_argument( "-r", "--rule", dest="rules", action="append", help= 'the automation you want to modify (see "Automation Rule Settings").\nYou can use * to modify all automations or enter the parameter multiple times to modify more automations', ) group = parser.add_argument_group("Device Settings") group.add_argument( "--turn-on", action="store_true", dest="device_switch_state", help="turn the switch on", default=None, ) group.add_argument( "--turn-off", action="store_false", dest="device_switch_state", help="turn the switch off", default=None, ) group.add_argument( "--set-dim-level", action="store", dest="device_dim_level", help="set dimmer to level (0..1)", default=None, ) group.add_argument( "--set-shutter-level", action="store", dest="device_shutter_level", help="set shutter to level (0..1)", ) group.add_argument( "--set-shutter-stop", action="store_true", dest="device_shutter_stop", help="stop shutter", default=None, ) group.add_argument("--set-label", dest="device_new_label", help="set a new label") group.add_argument( "--set-display", dest="device_display", action="store", help="set the display mode", choices=["actual", "setpoint", "actual_humidity"], ) group.add_argument( "--enable-router-module", action="store_true", dest="device_enable_router_module", help="enables the router module of the device", default=None, ) group.add_argument( "--disable-router-module", action="store_false", dest="device_enable_router_module", help="disables the router module of the device", default=None, ) group.add_argument( "--reset-energy-counter", action="store_true", dest="reset_energy_counter", help="resets the energy counter", ) group = parser.add_argument_group("Home Settings") group.add_argument( "--set-protection-mode", dest="protectionmode", action="store", help="set the protection mode", choices=["presence", "absence", "disable"], ) group.add_argument("--set-pin", dest="new_pin", action="store", help="set a new pin") group.add_argument("--delete-pin", dest="delete_pin", action="store_true", help="deletes the pin") group.add_argument( "--old-pin", dest="old_pin", action="store", help="the current pin. used together with --set-pin or --delete-pin", default=None, ) group.add_argument( "--set-zones-device-assignment", dest="set_zones_device_assignment", action="store_true", help="sets the zones devices assignment", ) group.add_argument( "--external-devices", dest="external_devices", nargs="+", help="sets the devices for the external zone", ) group.add_argument( "--internal-devices", dest="internal_devices", nargs="+", help="sets the devices for the internal zone", ) group.add_argument( "--activate-absence", dest="activate_absence", action="store", help="activates absence for provided amount of minutes", default=None, type=int, ) group.add_argument( "--deactivate-absence", action="store_true", dest="deactivate_absence", help="deactivates absence", ) group.add_argument( "--start-inclusion", action="store", dest="inclusion_device_id", help="start inclusion for device with given id", ) group = parser.add_argument_group("Group Settings") group.add_argument( "--list-profiles", dest="group_list_profiles", action="store_true", help="displays all profiles for a group", ) group.add_argument( "--activate-profile", dest="group_activate_profile", help="activates a profile by using its index or its name", ) group.add_argument( "--set-group-shutter-level", action="store", dest="group_shutter_level", help="set all shutters in group to level (0..1)", ) group.add_argument( "--set-group-shutter-stop", action="store_true", dest="group_shutter_stop", help="stop all shutters in group", default=None, ) group.add_argument( "--set-point-temperature", action="store", dest="group_set_point_temperature", help= 'sets the temperature for the given group. The group must be of the type "HEATING"', default=None, type=float, ) group.add_argument( "--set-boost", action="store_true", dest="group_boost", help="activates the boost mode for a HEATING group", default=None, ) group.add_argument( "--set-boost-stop", action="store_false", dest="group_boost", help="deactivates the boost mode for a HEATING group", default=None, ) group.add_argument( "--set-boost-duration", dest="group_boost_duration", action="store", help="sets the boost duration for a HEATING group in minutes", default=None, type=int, ) group = parser.add_argument_group("Automation Rule Settings") group.add_argument( "--enable-rule", action="store_true", dest="rule_activation", help="activates the automation rules", default=None, ) group.add_argument( "--disable-rule", action="store_false", dest="rule_activation", help="deactivates the automation rules", default=None, ) if len(sys.argv) == 1: parser.print_help() return try: args = parser.parse_args() except SystemExit: return except: print("could not parse arguments") parser.print_help() return _config = None if args.config_file: try: _config = homematicip.load_config_file(args.config_file) except FileNotFoundError: print("##### CONFIG FILE NOT FOUND: {} #####".format( args.config_file)) return else: _config = homematicip.find_and_load_config_file() if _config is None: print("Could not find configuration file. Script will exit") return global logger logger = create_logger( args.debug_level if args.debug_level else _config.log_level, _config.log_file) home = Home() home.set_auth_token(_config.auth_token) home.init(_config.access_point) command_entered = False if args.server_config: global server_config server_config = args.server_config home.download_configuration = fake_download_configuration if args.dump_config: command_entered = True json_state = home.download_configuration() output = handle_config(json_state, args.anonymize) if output: print(output) if not home.get_current_state(): return if args.list_devices: command_entered = True sortedDevices = sorted(home.devices, key=attrgetter("deviceType", "label")) for d in sortedDevices: print("{} {}".format(d.id, str(d))) if args.list_groups: command_entered = True sortedGroups = sorted(home.groups, key=attrgetter("groupType", "label")) for g in sortedGroups: print(g) if args.list_last_status_update: command_entered = True print("Devices:") sortedDevices = sorted(home.devices, key=attrgetter("deviceType", "label")) for d in sortedDevices: print("\t{}\t{}\t{}".format(d.id, d.label, d.lastStatusUpdate)) print("Groups:") sortedGroups = sorted(home.groups, key=attrgetter("groupType", "label")) for g in sortedGroups: print("\t{}\t{}\t{}".format(g.groupType, g.label, g.lastStatusUpdate)) if args.list_group_ids: command_entered = True sortedGroups = sorted(home.groups, key=attrgetter("groupType", "label")) for g in sortedGroups: print("Id: {} - Type: {} - Label: {}".format( g.id, g.groupType, g.label)) if args.protectionmode: command_entered = True if args.protectionmode == "presence": home.set_security_zones_activation(False, True) elif args.protectionmode == "absence": home.set_security_zones_activation(True, True) elif args.protectionmode == "disable": home.set_security_zones_activation(False, False) if args.new_pin: command_entered = True home.set_pin(args.new_pin, args.old_pin) if args.delete_pin: command_entered = True home.set_pin(None, args.old_pin) if args.list_security_journal: command_entered = True journal = home.get_security_journal() for entry in journal: print(entry) if args.list_firmware: command_entered = True print( "{:45s} - Firmware: {:6} - Available Firmware: {:6} UpdateState: {}" .format( "HmIP AccessPoint", home.currentAPVersion if home.currentAPVersion is not None else "None", home.availableAPVersion if home.availableAPVersion is not None else "None", home.updateState, )) sortedDevices = sorted(home.devices, key=attrgetter("deviceType", "label")) for d in sortedDevices: print( "{:45s} - Firmware: {:6} - Available Firmware: {:6} UpdateState: {} LiveUpdateState: {}" .format( d.label, d.firmwareVersion, d.availableFirmwareVersion if d.availableFirmwareVersion is not None else "None", d.updateState, d.liveUpdateState, )) if args.list_rssi: command_entered = True print("{:45s} - Duty cycle: {:2}".format( "HmIP AccessPoint", home.dutyCycle if home.dutyCycle is not None else "None", )) sortedDevices = sorted(home.devices, key=attrgetter("deviceType", "label")) for d in sortedDevices: print( "{:45s} - RSSI: {:4} {} - Peer RSSI: {:4} - {} {} permanentlyReachable: {}" .format( d.label, d.rssiDeviceValue if d.rssiDeviceValue is not None else "None", getRssiBarString(d.rssiDeviceValue), d.rssiPeerValue if d.rssiPeerValue is not None else "None", getRssiBarString(d.rssiPeerValue), "Unreachable" if d.unreach else "", d.permanentlyReachable, )) if args.list_rules: command_entered = True sortedRules = sorted(home.rules, key=attrgetter("ruleType", "label")) for d in sortedRules: print("{} {}".format(d.id, str(d))) if args.device: command_entered = False devices = [] for argdevice in args.device: if argdevice == "*": devices = home.devices break else: d = home.search_device_by_id(argdevice) if d == None: logger.error("Could not find device %s", argdevice) else: devices.append(d) for device in devices: if args.device_new_label: device.set_label(args.device_new_label) command_entered = True if args.device_switch_state is not None: if isinstance(device, Switch): device.set_switch_state(args.device_switch_state) else: logger.error( "can't turn on/off device %s of type %s", device.id, device.deviceType, ) command_entered = True if args.device_dim_level is not None: if isinstance(device, Dimmer): device.set_dim_level(args.device_dim_level) else: logger.error( "can't set dim level of device %s of type %s", device.id, device.deviceType, ) command_entered = True if args.device_shutter_level is not None: if isinstance(device, FullFlushShutter): device.set_shutter_level(args.device_shutter_level) else: logger.error( "can't set shutter level of device %s of type %s", device.id, device.deviceType, ) command_entered = True if args.device_shutter_stop is not None: if isinstance(device, FullFlushShutter): device.set_shutter_stop() else: logger.error( "can't stop shutter of device %s of type %s", device.id, device.deviceType, ) command_entered = True if args.device_display is not None: if isinstance(device, TemperatureHumiditySensorDisplay): device.set_display( ClimateControlDisplay(args.device_display.upper())) else: logger.error( "can't set display of device %s of type %s", device.id, device.deviceType, ) command_entered = True if args.device_enable_router_module is not None: if device.routerModuleSupported: device.set_router_module_enabled( args.device_enable_router_module) print("{} the router module for device {}".format( "Enabled" if args.device_enable_router_module else "Disabled", device.id, )) else: logger.error( "the device %s doesn't support the router module", device.id) command_entered = True if args.reset_energy_counter: if isinstance(device, SwitchMeasuring): device.reset_energy_counter() print("reset energycounter {}".format(device.id)) else: logger.error( "can't reset energy counter for device %s of type %s", device.id, device.deviceType, ) command_entered = True if args.set_zones_device_assignment: internal = [] external = [] error = False command_entered = True for id in args.external_devices: d = home.search_device_by_id(id) if d == None: logger.error( "Device %s is not registered on this Access Point", id) error = True else: external.append(d) for id in args.internal_devices: d = home.search_device_by_id(id) if d == None: logger.error( "Device %s is not registered on this Access Point", id) error = True else: internal.append(d) if not error: home.set_zones_device_assignment(internal, external) if args.activate_absence: command_entered = True home.activate_absence_with_duration(args.activate_absence) if args.deactivate_absence: command_entered = True home.deactivate_absence() if args.inclusion_device_id: command_entered = True home.start_inclusion(args.inclusion_device_id) if args.group: command_entered = False group = None for g in home.groups: if g.id == args.group: group = g break if group == None: logger.error("Could not find group %s", args.group) return if args.group_list_profiles: command_entered = True for p in group.profiles: isActive = p.id == group.activeProfile.id print("Index: {} - Id: {} - Name: {} - Active: {}".format( p.index, p.id, p.name, isActive)) if args.group_shutter_level: command_entered = True group.set_shutter_level(args.group_shutter_level) if args.group_shutter_stop: command_entered = True group.set_shutter_stop() if args.group_set_point_temperature: command_entered = True if isinstance(group, HeatingGroup): group.set_point_temperature(args.group_set_point_temperature) else: logger.error("Group %s isn't a HEATING group", g.id) if args.group_activate_profile: command_entered = True if isinstance(group, HeatingGroup): index = args.group_activate_profile for p in group.profiles: if p.name == args.group_activate_profile: index = p.index break group.set_active_profile(index) else: logger.error("Group %s isn't a HEATING group", g.id) if args.group_boost is not None: command_entered = True if isinstance(group, HeatingGroup): group.set_boost(args.group_boost) else: logger.error("Group %s isn't a HEATING group", g.id) if args.group_boost_duration is not None: command_entered = True if isinstance(group, HeatingGroup): group.set_boost_duration(args.group_boost_duration) else: logger.error("Group %s isn't a HEATING group", g.id) if args.rules: command_entered = False rules = [] for argrule in args.rules: if argrule == "*": rules = home.rules break else: r = home.search_rule_by_id(argrule) if r == None: logger.error("Could not find automation rule %s", argrule) else: rules.append(r) for rule in rules: if args.rule_activation is not None: if isinstance(rule, SimpleRule): rule.set_rule_enabled_state(args.rule_activation) command_entered = True else: logger.error( "can't enable/disable rule %s of type %s", rule.id, rule.ruleType, ) if args.list_events: command_entered = True home.onEvent += printEvents home.enable_events() try: while True: time.sleep(1) except KeyboardInterrupt: return if not command_entered: parser.print_help()
class Exporter(object): """ Prometheus Exporter for Homematic IP devices """ def __init__(self, args): """ initializes the exporter :param args: the argparse.Args """ self.__home_client = None self.__metric_port = int(args.metric_port) self.__collect_interval_seconds = args.collect_interval_seconds self.__log_level = int(args.log_level) logging.info( "using config file '{}' and exposing metrics on port '{}'".format( args.config_file, self.__metric_port)) self.__init_client(args.config_file, args.auth_token, args.access_point, args.enable_event_metrics) self.__init_metrics() self.__collect_homematicip_info() try: prometheus_client.start_http_server(self.__metric_port) except Exception as e: logging.fatal( "starting the http server on port '{}' failed with: {}".format( self.__metric_port, str(e))) sys.exit(1) def __init_client(self, config_file, auth_token, access_point, enable_event_metrics): if auth_token and access_point: config = homematicip.HmipConfig( auth_token=auth_token, access_point=access_point, log_level=self.__log_level, log_file='hmip.log', raw_config=None, ) else: config = homematicip.load_config_file(config_file=config_file) try: self.__home_client = Home() self.__home_client.set_auth_token(config.auth_token) self.__home_client.init(config.access_point) # metrics on events if enable_event_metrics: self.__home_client.onEvent += self.__collect_event_metrics self.__home_client.enable_events() except Exception as e: logging.fatal( "Initializing HomematicIP client failed with: {}".format( str(e))) sys.exit(1) def __init_metrics(self): namespace = 'homematicip' labelnames = ['room', 'device_label'] detail_labelnames = [ 'device_type', 'firmware_version', 'permanently_reachable' ] event_device_labelnames = ['device_label'] event_group_labelnames = ['group_label'] event_labelnames = ['type', 'window_state', 'sabotage'] self.version_info = prometheus_client.Gauge( name='version_info', documentation='HomematicIP info', labelnames=['api_version'], namespace=namespace) self.metric_temperature_actual = prometheus_client.Gauge( name='temperature_actual', documentation='Actual temperature', labelnames=labelnames, namespace=namespace) self.metric_temperature_setpoint = prometheus_client.Gauge( name='temperature_setpoint', documentation='Set point temperature', labelnames=labelnames, namespace=namespace) self.metric_valve_adaption_needed = prometheus_client.Gauge( name='valve_adaption_needed', documentation='must the adaption re-run?', labelnames=labelnames, namespace=namespace) self.metric_temperature_offset = prometheus_client.Gauge( name='temperature_offset', documentation='the offset temperature for the thermostat', labelnames=labelnames, namespace=namespace) self.metric_valve_position = prometheus_client.Gauge( name='valve_position', documentation= 'the current position of the valve 0.0 = closed, 1.0 max opened', labelnames=labelnames, namespace=namespace) self.metric_humidity_actual = prometheus_client.Gauge( name='humidity_actual', documentation='Actual Humidity', labelnames=labelnames, namespace=namespace) self.metric_last_status_update = prometheus_client.Gauge( name='last_status_update', documentation="Device last status update", labelnames=labelnames, namespace=namespace) self.metric_device_info = prometheus_client.Gauge( name='device_info', documentation='Device information', labelnames=labelnames + detail_labelnames, namespace=namespace) self.metric_power_consumption = prometheus_client.Gauge( name='power_consumption', documentation='Power consumption', labelnames=labelnames, namespace=namespace) self.metric_device_event = prometheus_client.Counter( name='device_event', documentation='events triggered by a device', labelnames=event_device_labelnames + event_labelnames, namespace=namespace) self.metric_group_event = prometheus_client.Counter( name='group_event', documentation='events triggered by a group', labelnames=event_group_labelnames + event_labelnames, namespace=namespace, ) def __collect_homematicip_info(self): try: self.version_info.labels( api_version=self.__home_client.currentAPVersion).set(1) logging.debug("current homematic ip api version: '{}'".format( self.__home_client.currentAPVersion)) except Exception as e: logging.warning("collecting version info failed with: {}".format( str(e))) def __collect_thermostat_metrics(self, room, device): if device.actualTemperature: self.metric_temperature_actual.labels( room=room, device_label=device.label).set(device.actualTemperature) if device.setPointTemperature: self.metric_temperature_setpoint.labels( room=room, device_label=device.label).set(device.setPointTemperature) if device.humidity: self.metric_humidity_actual.labels(room=room, device_label=device.label).set( device.humidity) logging.info( "room: {}, label: {}, temperature_actual: {}, temperature_setpoint: {}, humidity_actual: {}" .format(room, device.label, device.actualTemperature, device.setPointTemperature, device.humidity)) def __collect_heating_metrics(self, room, device): # Do not check with if as 0 equals false self.metric_temperature_actual.labels( room=room, device_label=device.label).set(device.valveActualTemperature) self.metric_temperature_setpoint.labels(room=room, device_label=device.label).set( device.setPointTemperature) self.metric_valve_adaption_needed.labels( room=room, device_label=device.label).set(device.automaticValveAdaptionNeeded) self.metric_temperature_offset.labels(room=room, device_label=device.label).set( device.temperatureOffset) self.metric_valve_position.labels( room=room, device_label=device.label).set(device.valvePosition) logging.info( "room: {}, label: {}, temperature_actual: {}, temperature_setpoint: {}, valve_adaption_needed: {}, " "temperature_offset {}, valve_position: {}".format( room, device.label, device.valveActualTemperature, device.setPointTemperature, device.automaticValveAdaptionNeeded, device.temperatureOffset, device.valvePosition)) def __collect_device_info_metrics(self, room, device): logging.info( "found device: room: {}, label: {}, device_type: {}, firmware_version: {}, last_status_update: {}, permanently_reachable: {}" .format(room, device.label, device.deviceType.lower(), device.firmwareVersion, device.lastStatusUpdate, device.permanentlyReachable)) # general device info metric self.metric_device_info.labels( room=room, device_label=device.label, device_type=device.deviceType.lower(), firmware_version=device.firmwareVersion, permanently_reachable=device.permanentlyReachable).set(1) if device.lastStatusUpdate: # last status update metric self.metric_last_status_update.labels( room=room, device_label=device.label).set( device.lastStatusUpdate.timestamp()) def __collect_power_metrics(self, room, device): logging.info( "found device: room: {}, label: {}, device_type: {}, firmware_version: {}, last_status_update: {}, permanently_reachable: {}" .format(room, device.label, device.deviceType.lower(), device.firmwareVersion, device.lastStatusUpdate, device.permanentlyReachable)) # general device info metric logging.info(device.currentPowerConsumption) self.metric_power_consumption.labels( room=room, device_label=device.label).set(device.currentPowerConsumption) def __collect_event_metrics(self, eventList): for event in eventList: type = event["eventType"] data = event["data"] if type is EventType.DEVICE_CHANGED: _window_state = _sabotage = None if isinstance(data, ShutterContact): _window_state = str(data.windowState).lower() _sabotage = str(data.sabotage).lower() self.metric_device_event.labels(device_label=data.label, type=str(type).lower(), window_state=_window_state, sabotage=_sabotage).inc() logging.info( "got device event type: {}, label: {}, window_state: {}, sabotage: {}" .format(type, data.label, _window_state, _sabotage)) def collect(self): """ collect discovers all devices and generates metrics """ try: self.__home_client.get_current_state() for g in self.__home_client.groups: if g.groupType == "META": for d in g.devices: # collect general device metrics self.__collect_device_info_metrics(g.label, d) # collect temperature, humidity if isinstance(d, (WallMountedThermostatPro, TemperatureHumiditySensorDisplay, TemperatureHumiditySensorWithoutDisplay, TemperatureHumiditySensorOutdoor)): self.__collect_thermostat_metrics(g.label, d) elif isinstance(d, HeatingThermostat): logging.info("Device of type heating") self.__collect_heating_metrics(g.label, d) elif isinstance(d, PlugableSwitchMeasuring): logging.info( "Device of type PlugableSwitchMeasuring") self.__collect_power_metrics(g.label, d) except Exception as e: logging.warning( "collecting status from device(s) failed with: {1}".format( str(e))) finally: logging.info('waiting {}s before next collection cycle'.format( self.__collect_interval_seconds)) time.sleep(self.__collect_interval_seconds)
def main(): parser = ArgumentParser( description="a cli wrapper for the homematicip API") parser.add_argument( "--config_file", type=str, help= "the configuration file. If nothing is specified the script will search for it." ) parser.add_argument( "--debug-level", dest="debug_level", type=int, help="the debug level which should get used(Critical=50, DEBUG=10)") group = parser.add_argument_group("Display Configuration") group.add_argument("--dump-configuration", action="store_true", dest="dump_config", help="dumps the current configuration from the AP") group.add_argument("--list-devices", action="store_true", dest="list_devices", help="list all devices") group.add_argument("--list-groups", action="store_true", dest="list_groups", help="list all groups") group.add_argument("--list-group-ids", action="store_true", dest="list_group_ids", help="list all groups and their ids") group.add_argument("--list-firmware", action="store_true", dest="list_firmware", help="list the firmware of all devices") group.add_argument("--list-rssi", action="store_true", dest="list_rssi", help="list the reception quality of all devices") group.add_argument("--list-events", action="store_true", dest="list_events", help="prints all the events") group.add_argument("--list-last-status-update", action="store_true", dest="list_last_status_update", help="prints the last status update of all systems") parser.add_argument("--list-security-journal", action="store_true", dest="list_security_journal", help="display the security journal") parser.add_argument("--list-rules", action="store_true", dest="list_rules", help="display all automation rules") parser.add_argument( "-d", "--device", dest="device", action='append', help= "the device you want to modify (see \"Device Settings\").\nYou can use * to modify all devices or enter the parameter multiple times to modify more devices" ) parser.add_argument( "-g", "--group", dest="group", help="the group you want to modify (see \"Group Settings\")") group = parser.add_argument_group("Device Settings") group.add_argument("--turn-on", action="store_true", dest="device_switch_state", help="turn the switch on", default=None) group.add_argument("--set-dimmer", action="store", dest="set_dimmer", help="turn the switch on", default=None) group.add_argument("--turn-off", action="store_false", dest="device_switch_state", help="turn the switch off", default=None) group.add_argument("--set-shutter-level", action="store", dest="device_shutter_level", help="set shutter to level (0..1)") group.add_argument("--set-shutter-stop", action="store_true", dest="device_shutter_stop", help="stop shutter", default=None) group.add_argument("--set-label", dest="device_new_label", help="set a new label") group.add_argument("--set-display", dest="device_display", action="store", help="set the display mode", choices=["actual", "setpoint", "actual_humidity"]) group.add_argument("--enable-router-module", action="store_true", dest="device_enable_router_module", help="enables the router module of the device", default=None) group.add_argument("--disable-router-module", action="store_false", dest="device_enable_router_module", help="disables the router module of the device", default=None) group = parser.add_argument_group("Home Settings") group.add_argument("--set-protection-mode", dest="protectionmode", action="store", help="set the protection mode", choices=["presence", "absence", "disable"]) group.add_argument("--set-pin", dest="new_pin", action="store", help="set a new pin") group.add_argument("--delete-pin", dest="delete_pin", action="store_true", help="deletes the pin") group.add_argument( "--old-pin", dest="old_pin", action="store", help="the current pin. used together with --set-pin or --delete-pin", default=None) group.add_argument("--set-zones-device-assignment", dest="set_zones_device_assignment", action="store_true", help="sets the zones devices assignment") group.add_argument("--external-devices", dest="external_devices", nargs='+', help="sets the devices for the external zone") group.add_argument("--internal-devices", dest="internal_devices", nargs='+', help="sets the devices for the internal zone") group.add_argument("--activate-absence", dest="activate_absence", action="store", help="activates absence for provided amount of minutes", default=None, type=int) group.add_argument("--deactivate-absence", action="store_true", dest="deactivate_absence", help="deactivates absence") group = parser.add_argument_group("Group Settings") group.add_argument("--list-profiles", dest="group_list_profiles", action="store_true", help="displays all profiles for a group") group.add_argument( "--activate-profile", dest="group_activate_profile", help="activates a profile by using its index or its name") group.add_argument("--set-group-shutter-level", action="store", dest="group_shutter_level", help="set all shutters in group to level (0..1)") group.add_argument("--set-group-shutter-stop", action="store_true", dest="group_shutter_stop", help="stop all shutters in group", default=None) group.add_argument( "--set-point-temperature", action="store", dest="group_set_point_temperature", help= "sets the temperature for the given group. The group must be of the type \"HEATING\"", default=None, type=float) group.add_argument("--set-boost", action="store_true", dest="group_boost", help="activates the boost mode for a HEATING group", default=None) group.add_argument("--set-boost-stop", action="store_false", dest="group_boost", help="deactivates the boost mode for a HEATING group", default=None) if len(sys.argv) == 1: parser.print_help() return try: args = parser.parse_args() except: print('could not parse arguments') parser.print_help() return _config = None if args.config_file: try: _config = homematicip.load_config_file(args.config_file) except FileNotFoundError: print("##### CONFIG FILE NOT FOUND: {} #####".format( args.config_file)) return else: convert_config2ini() _config = homematicip.find_and_load_config_file() if _config is None: print("Could not find configuration file. Script will exit") return global logger logger = create_logger( args.debug_level if args.debug_level else _config.log_level, _config.log_file) home = Home() home.set_auth_token(_config.auth_token) home.init(_config.access_point) if not home.get_current_state(): return command_entered = False if args.dump_config: command_entered = True json_state = home.download_configuration() if "errorCode" in json_state: logger.error( "Could not get the current configuration. Error: {}".format( json_state["errorCode"])) else: print(json.dumps(json_state, indent=4, sort_keys=True)) if args.list_devices: command_entered = True sortedDevices = sorted(home.devices, key=attrgetter('deviceType', 'label')) for d in sortedDevices: print(u'{} {}'.format(d.id, str(d))) if args.list_groups: command_entered = True sortedGroups = sorted(home.groups, key=attrgetter('groupType', 'label')) for g in sortedGroups: print(str(g)) if args.list_last_status_update: command_entered = True print(u'Devices:') sortedDevices = sorted(home.devices, key=attrgetter('deviceType', 'label')) for d in sortedDevices: print(u'\t{}\t{}\t{}'.format(d.id, d.label, d.lastStatusUpdate)) print(u'Groups:') sortedGroups = sorted(home.groups, key=attrgetter('groupType', 'label')) for g in sortedGroups: print(u'\t{}\t{}\t{}'.format(g.groupType, g.label, g.lastStatusUpdate)) if args.list_group_ids: command_entered = True sortedGroups = sorted(home.groups, key=attrgetter('groupType', 'label')) for g in sortedGroups: print("Id: {} - Type: {} - Label: {}".format( g.id, g.groupType, g.label)) if args.protectionmode: command_entered = True if args.protectionmode == "presence": home.set_security_zones_activation(False, True) elif args.protectionmode == "absence": home.set_security_zones_activation(True, True) elif args.protectionmode == "disable": home.set_security_zones_activation(False, False) if args.new_pin: command_entered = True home.set_pin(args.new_pin, args.old_pin) if args.delete_pin: command_entered = True home.set_pin(None, args.old_pin) if args.list_security_journal: command_entered = True journal = home.get_security_journal() for entry in journal: print(str(entry)) if args.list_firmware: command_entered = True print( str("{:45s} - Firmware: {:6} - Available Firmware: {:6} UpdateState: {}" .format( "HmIP AccessPoint", home.currentAPVersion if home.currentAPVersion is not None else "None", home.availableAPVersion if home.availableAPVersion is not None else "None", home.updateState))) sortedDevices = sorted(home.devices, key=attrgetter('deviceType', 'label')) for d in sortedDevices: print( str("{:45s} - Firmware: {:6} - Available Firmware: {:6} UpdateState: {}" .format( d.label, d.firmwareVersion, d.availableFirmwareVersion if d.availableFirmwareVersion is not None else "None", d.updateState))) if args.list_rssi: command_entered = True print( str("{:45s} - Duty cycle: {:2}".format( "HmIP AccessPoint", home.dutyCycle if home.dutyCycle is not None else "None"))) sortedDevices = sorted(home.devices, key=attrgetter('deviceType', 'label')) for d in sortedDevices: print( str("{:45s} - RSSI: {:4} {} - Peer RSSI: {:4} - {} {}".format( d.label, d.rssiDeviceValue if d.rssiDeviceValue is not None else "None", getRssiBarString(d.rssiDeviceValue), d.rssiPeerValue if d.rssiPeerValue is not None else "None", getRssiBarString(d.rssiPeerValue), "Unreachable" if d.unreach else ""))) if args.list_rules: command_entered = True sortedRules = sorted(home.rules, key=attrgetter('ruleType', 'label')) for d in sortedRules: print(u'{} {}'.format(d.id, str(d))) if args.device: command_entered = False devices = [] for argdevice in args.device: if argdevice == '*': devices = home.devices break else: d = home.search_device_by_id(argdevice) if d == None: logger.error("Could not find device {}".format(argdevice)) else: devices.append(d) for device in devices: if args.device_new_label: device.set_label(args.device_new_label) command_entered = True if args.device_switch_state != None: if isinstance(device, PlugableSwitch): device.set_switch_state(args.device_switch_state) command_entered = True else: logger.error( "can't turn on/off device {} of type {}".format( device.id, device.deviceType)) if args.set_dimmer is not None: if isinstance(device, BrandDimmer): device.set_dim_level(args.set_dimmer) command_entered = True else: logger.error("can't set device {} of type {}".format( device.id, device.deviceType)) if args.device_shutter_level is not None: if isinstance(device, FullFlushShutter): device.set_shutter_level(args.device_shutter_level) command_entered = True else: logger.error( "can't set shutter level of device {} of type {}". format(device.id, device.deviceType)) if args.device_shutter_stop is not None: if isinstance(device, FullFlushShutter): device.set_shutter_stop() command_entered = True else: logger.error( "can't stop shutter of device {} of type {}".format( device.id, device.deviceType)) if args.device_display != None: if isinstance(device, TemperatureHumiditySensorDisplay): device.set_display(args.device_display.upper()) command_entered = True else: logger.error( "can't set display of device {} of type {}".format( device.id, device.deviceType)) if args.device_enable_router_module != None: if device.routerModuleSupported: device.set_router_module_enabled( args.device_enable_router_module) print("{} the router module for device {}".format( "Enabled" if args.device_enable_router_module else "Disabled", device.id)) command_entered = True else: logger.error( "the device {} doesn't support the router module". format(device.id)) if args.set_zones_device_assignment: internal = [] external = [] error = False command_entered = True for id in args.external_devices: d = home.search_device_by_id(id) if d == None: logger.error( "Device {} is not registered on this Access Point".format( id)) error = True else: external.append(d) for id in args.internal_devices: d = home.search_device_by_id(id) if d == None: logger.error( "Device {} is not registered on this Access Point".format( id)) error = True else: internal.append(d) if not error: home.set_zones_device_assignment(internal, external) if args.activate_absence: command_entered = True home.activate_absence_with_duration(args.activate_absence) if args.deactivate_absence: command_entered = True home.deactivate_absence() if args.group: command_entered = False group = None for g in home.groups: if g.id == args.group: group = g break if group == None: logger.error("Could not find group {}".format(args.group)) return if args.group_list_profiles: command_entered = True for p in group.profiles: isActive = p.id == group.activeProfile.id print("Index: {} - Id: {} - Name: {} - Active: {}".format( p.index, p.id, p.name, isActive)) if args.group_shutter_level: command_entered = True group.set_shutter_level(args.group_shutter_level) if args.group_shutter_stop: command_entered = True group.set_shutter_stop() if args.group_set_point_temperature: command_entered = True if isinstance(group, HeatingGroup): group.set_point_temperature(args.group_set_point_temperature) else: logger.error("Group {} isn't a HEATING group".format(g.id)) if args.group_activate_profile: command_entered = True if isinstance(group, HeatingGroup): index = args.group_activate_profile for p in group.profiles: if p.name == args.group_activate_profile: index = p.index break group.set_active_profile(index) else: logger.error("Group {} isn't a HEATING group".format(g.id)) if args.group_boost is not None: command_entered = True if isinstance(group, HeatingGroup): group.set_boost(args.group_boost) else: logger.error("Group {} isn't a HEATING group".format(g.id)) if args.list_events: command_entered = True home.onEvent += printEvents home.enable_events() try: while True: time.sleep(1) except KeyboardInterrupt: return if not command_entered: parser.print_help()
def test_websocket_security_journal_changed(fake_home: Home, home_data): fake_home.enable_events() send_event(fake_home, EventType.SECURITY_JOURNAL_CHANGED, None, None) time.sleep(1) fake_home.disable_events()