Skip to content

Commit

Permalink
Formatted output in cli
Browse files Browse the repository at this point in the history
  • Loading branch information
hahn-th committed Jun 16, 2024
1 parent aa3b73f commit a0d8443
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 41 deletions.
122 changes: 122 additions & 0 deletions data/output-format.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"functional_channel_attributes": [
"accelerationSensorEventFilterPeriod",
"accelerationSensorMode",
"accelerationSensorNeutralPosition",
"accelerationSensorSensitivity",
"accelerationSensorTriggerAngle",
"accelerationSensorTriggered",
"actualTemperature",
"alarmContactType",
"authorized",
"averageIllumination",
"binaryBehaviorType",
"blindModeActive",
"colorTemperature",
"currentIllumination",
"currentPowerConsumption",
"dimLevel",
"display",
"doorHandleType",
"doorLockDirection",
"doorState",
"energyCounter",
"genericAlarmSignal",
"highestIllumination",
"humidity",
"illumination",
"impulseDuration",
"leftCounter",
"leftRightCounterDelta",
"lowBat",
"lowestIllumination",
"moistureDetected",
"motionBufferActive",
"motionDetected",
"motorState",
"multiModeInputMode",
"notificationSoundTypeHighToLow",
"notificationSoundTypeLowToHigh",
"on",
"passageBlindtime",
"passageDirection",
"passageSensorSensitivity",
"powerMainsFailure",
"previousShutterLevel",
"previousSlatsLevel",
"primaryShadingLevel",
"processing",
"profileMode",
"pumpFollowUpTime",
"pumpLeadTime",
"pumpProtectionDuration",
"pumpProtectionSwitchingInterval",
"raining",
"rightCounter",
"rssiDeviceValue",
"rssiPeerValue",
"saturationLevel",
"secondaryShadingLevel",
"selfCalibrationInProgress",
"setPointTemperature",
"shutterLevel",
"simpleRGBColorState",
"slatsLevel",
"smokeDetectorAlarmType",
"storm",
"sunshine",
"temperatureExternalDelta",
"temperatureExternalOne",
"temperatureExternalTwo",
"temperatureOffset",
"todayRainCounter",
"todaySunshineDuration",
"totalRainCounter",
"totalSunshineDuration",
"unreach",
"valveActualTemperature",
"valvePosition",
"valveState",
"vaporAmount",
"waterlevelDetected",
"windSpeed",
"windowState",
"yesterdayRainCounter",
"yesterdaySunshineDuration"
],
"group_attributes": [
"lowBat",
"unreach",
"dutyCycle",
"incorrectPositioned",
"activeProfile",
"actualTemperature",
"boostDuration",
"boostMode",
"humidity",
"partyMode",
"valvePosition",
"windowState",
"motionDetected",
"presenceDetected",
"on",
"triggered",
"primaryShadingLevel",
"secondaryShadingLevel",
"shutterLevel",
"slatsLevel",
"moistureDetected",
"waterLevelDetected",
"signalAcoustic",
"signalOptical",
"illumination",
"enabled",
"ventilationLevel"
],
"device_attributes": [
"connectionType",
"modelType",
"permanentlyReachable",
"externalService"
]
}
6 changes: 3 additions & 3 deletions src/homematicip/cli/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ async def get_initialized_runner(including_model: bool = True) -> Runner:

def get_config() -> Config:
"""Get Config object from the configuration file. If no configuration file is found, raise an exception."""
config = ConfigIO.find_config_in_well_known_locations()
persistent_config = ConfigIO.find_config_in_well_known_locations()

if config is None:
if persistent_config is None:
raise ClickException(
"No configuration file found. Run hmip auth to get an auth token.")

return Config.from_persistent_config(config)
return Config.from_persistent_config(persistent_config)


def setup_basic_logging(log_level: int, logger_filename: str = None) -> None:
Expand Down
45 changes: 28 additions & 17 deletions src/homematicip/cli/hmip.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
is_device, get_device_or_group, is_group, get_channel_name, get_device_name, get_group_name
from homematicip.cli.helper import get_rssi_bar_string
from homematicip.cli.hmip_cli_show_targets_helper import build_commands_from_registry, CommandEntry
from homematicip.cli.output_formatter.formatter import generate_output_functional_channel, generate_output_group, \
generate_output_device
from homematicip.configuration import config_io
from homematicip.configuration.config import PersistentConfig
from homematicip.configuration.config_folder import get_default_app_config_folder
from homematicip.configuration.config_io import ConfigIO
Expand Down Expand Up @@ -142,22 +145,30 @@ def devices():
"""List all devices including the functional channels."""
runner = asyncio.run(get_initialized_runner())
model = runner.model
format_config = ConfigIO.get_output_format_config()
print("Devices:")
for device in sorted(model.devices.values(), key=lambda x: x.label):
output = generate_output_device(device, format_config)
click.echo(f"\t({device.id}) - {device.label} - {device.type}")
click.echo(f"\t{output}")

for channel in device.functionalChannels.values():
fc_output = generate_output_functional_channel(channel, format_config)
click.echo(f"\t\t[{channel.index}] - {channel.functionalChannelType:30} - {channel.label or "<no label>"}")
click.echo(f"\t\t\t{output}")


@list.command()
def groups():
"""List all groups."""
runner = asyncio.run(get_initialized_runner())
model = runner.model
format_config = ConfigIO.get_output_format_config()
print("Groups:")
for group in sorted(model.groups.values(), key=lambda x: x.label):
output = generate_output_group(group, format_config)
click.echo(f"\t({group.id}) - {group.label} - {group.type}")
click.echo(f"\t\t{output}")


@list.command()
Expand Down Expand Up @@ -317,7 +328,7 @@ def version():


@run.command()
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand All @@ -342,7 +353,7 @@ def turn_on(id: str, channel: int = None):


@run.command()
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand Down Expand Up @@ -370,7 +381,7 @@ def turn_off(id: str, channel: int = None):


@run.command()
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand Down Expand Up @@ -417,7 +428,7 @@ def dump(filename, anonymized):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
def set_boost(id: str):
"""Set Boost on Group."""
runner = asyncio.run(get_initialized_runner())
Expand All @@ -431,7 +442,7 @@ def set_boost(id: str):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
def set_boost_stop(id: str):
"""Stop Boost on Group."""
runner = asyncio.run(get_initialized_runner())
Expand All @@ -447,7 +458,7 @@ def set_boost_stop(id: str):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-t", "--temperature", type=float, help="Target Temperature", required=True)
def set_point_temperature(id: str, temperature: float):
"""Set point temperature for a group."""
Expand All @@ -469,7 +480,7 @@ def set_point_temperature(id: str, temperature: float):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-m", "--minutes", type=int, nargs=1, help="Duration of boost in Minutes", required=True)
def set_boost_duration(id: str, minutes: int):
"""Sets the boost duration for a group in minutes"""
Expand All @@ -486,7 +497,7 @@ def set_boost_duration(id: str, minutes: int):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-p", "--profile_index", type=str, required=True,
help="index of the profile. Usually this is PROFILE_x. Use 'hmip list profiles <group-id>' to get a "
"list of available profiles for a group.")
Expand All @@ -506,7 +517,7 @@ def set_active_profile(id: str, profile_index: str):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("--mode", type=ClimateControlMode, help="the control mode. [ECO|AUTOMATIC|MANUAL]")
def set_control_mode(id: str, mode: ClimateControlMode):
"""Set the control mode for a group."""
Expand All @@ -523,7 +534,7 @@ def set_control_mode(id: str, mode: ClimateControlMode):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand All @@ -545,7 +556,7 @@ def set_display(id: str, channel: int, display: str):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-d", "--dim_level", type=click.FloatRange(0.0, 1.0), help="Target Dim Level", required=True)
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
Expand Down Expand Up @@ -576,7 +587,7 @@ def set_dim_level(id: str, dim_level: float, channel: int = None):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand Down Expand Up @@ -604,7 +615,7 @@ def set_shutter_level(id: str, shutter_level: float, channel: int):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand All @@ -631,7 +642,7 @@ def set_shutter_stop(id: str, shutter_level: float, channel: int):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand Down Expand Up @@ -661,7 +672,7 @@ def set_slats_level(id: str, slats_level: float, shutter_level: float = None, ch


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-t", "--on_time", type=int, required=True, help="On time in seconds.")
def set_on_time(id: str, on_time: int):
"""Set the on time for a group."""
Expand All @@ -678,7 +689,7 @@ def set_on_time(id: str, on_time: int):


@run.command
@click.option("--id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=True, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Only necessary, if you have more than one channel on the device. Not "
"needed, if you want to control a group.")
Expand All @@ -701,7 +712,7 @@ def toggle_garage_door(id: str, channel: int = None):


@run.command
@click.option("--id", type=str, required=False, help="ID of the device or group, which the run command is applied to.")
@click.option("-id", type=str, required=False, help="ID of the device or group, which the run command is applied to.")
@click.option("-c", "--channel", type=int, required=False, default=None,
help="Index of the Channel. Specify the channel index of a device, if you want to see available commands"
"just for a single channel.")
Expand Down
Empty file.
26 changes: 26 additions & 0 deletions src/homematicip/cli/output_formatter/formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from homematicip.configuration.output_format_config import OutputFormatConfig
from homematicip.model.model_components import FunctionalChannel


def generate_output_functional_channel(fc: FunctionalChannel, output_format_config: OutputFormatConfig) -> str:
"""Generate a string representation of a FunctionalChannel object based on the output format configuration."""
return _generate_output(fc, output_format_config.functional_channel_attributes)


def generate_output_group(group, output_format_config: OutputFormatConfig) -> str:
"""Generate a string representation of a Group object based on the output format configuration."""
return _generate_output(group, output_format_config.group_attributes)


def generate_output_device(device, output_format_config: OutputFormatConfig) -> str:
"""Generate a string representation of a Device object based on the output format configuration."""
return _generate_output(device, output_format_config.device_attributes)

def _generate_output(group, attributes):
outputs = []

for attrib_name in attributes:
if hasattr(group, attrib_name):
outputs.append(f"{attrib_name}: {getattr(group, attrib_name)}")

return ", ".join(outputs)
5 changes: 3 additions & 2 deletions src/homematicip/configuration/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
import logging
from typing import List


@dataclass
Expand All @@ -20,4 +21,4 @@ def from_persistent_config(cls, persistent_config: PersistentConfig):
return cls(level=persistent_config.level,
accesspoint_id=persistent_config.accesspoint_id,
auth_token=persistent_config.auth_token,
log_file=persistent_config.log_file)
log_file=persistent_config.log_file)
Loading

0 comments on commit a0d8443

Please sign in to comment.