From bbb53e450ce0a156e6476d87d66497f1193ebea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:54:23 +0200 Subject: [PATCH 01/20] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b0ad866..9980cb6 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ There are probably many issues to list... - There are (almost) no unit tests :( - Logging is pretty much non-existent, documentation is a bit lacking - ankerctl can crash sometimes, hindering the integration from working until it's restarted +- Not sure if multiple printers on one ankerctl instance will work, I doubt it. ## Testing From 128211ad55ef5951a3dd8406ff199e6d34e6534d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Tue, 9 Jul 2024 21:31:24 +0200 Subject: [PATCH 02/20] Add API status (not present in main branch of ankerctl yet) --- README.md | 7 +- custom_components/ankermake/__init__.py | 6 +- custom_components/ankermake/ankerctl_util.py | 16 ++++ .../ankermake/ankermake_mqtt_adapter.py | 22 ++++- custom_components/ankermake/binary_sensor.py | 14 +++- custom_components/ankermake/sensor.py | 9 ++- .../ankermake/sensor_manifest.py | 80 ++++++++++++++++++- 7 files changed, 142 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9980cb6..7ce3071 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ Home Assistant UI by searching for "AnkerMake" or click the button below. [![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=ankermake) -> Note: You can add as many instances as you'd like (but you will need an ankerctl instance running for each one). +> Note: You can add as many instances as you'd like (but you will need an ankerctl instance configured for each +> printer). ## Dependencies @@ -37,6 +38,9 @@ For this component to work, you will need an instance of [ankerctl](https://gith running and working. Please refer to the ankerctl documentation for installation instructions. (They do have a Home Assistant add-on in their organization, but I have not tested it with this component). +(The branch of ankerctl I'm +using: https://github.com/sondregronas/ankermake-m5-protocol/tree/patch-exiles-1.1-auto-restart-on-failure) + ## Known issues There are probably many issues to list... @@ -51,7 +55,6 @@ There are probably many issues to list... - There are (almost) no unit tests :( - Logging is pretty much non-existent, documentation is a bit lacking - ankerctl can crash sometimes, hindering the integration from working until it's restarted -- Not sure if multiple printers on one ankerctl instance will work, I doubt it. ## Testing diff --git a/custom_components/ankermake/__init__.py b/custom_components/ankermake/__init__.py index 67cc45b..74f736e 100644 --- a/custom_components/ankermake/__init__.py +++ b/custom_components/ankermake/__init__.py @@ -23,6 +23,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, CoordinatorEntity from .anker_models import AnkerException +from .ankerctl_util import get_api_status from .ankermake_mqtt_adapter import AnkerData from .const import DOMAIN, STARTUP, UPDATE_FREQUENCY_SECONDS @@ -101,10 +102,13 @@ def on_message(message): await session.close() async def _async_update_data(self): + try: + self.ankerdata._api_status = await get_api_status(self.config['host']) + except AnkerException as e: + _LOGGER.debug(f"[AnkerMake] Error updating API data: {e}") # Ensure task is still running if self._listen_to_ws_task.done(): self._listen_to_ws_task = asyncio.create_task(self._listen_to_ws()) - await asyncio.sleep(5) # If the task dies, wait 5 seconds before trying again class AnkerMakeBaseEntity(CoordinatorEntity[AnkerMakeUpdateCoordinator]): diff --git a/custom_components/ankermake/ankerctl_util.py b/custom_components/ankermake/ankerctl_util.py index 064193b..c3e87eb 100644 --- a/custom_components/ankermake/ankerctl_util.py +++ b/custom_components/ankermake/ankerctl_util.py @@ -72,3 +72,19 @@ async def reload_ankerctl(host: str): raise AnkerUtilException(f"Failed to reload ankerctl: {response.status}") except Exception as e: raise AnkerUtilException(f"Failed to reload ankerctl: {e}") + + +async def get_api_status(host: str): + """Gets the status of the ankerctl api.""" + url = host.replace("ws://", "http://").replace("wss://", "https://") + try: + async with aiohttp.ClientSession() as session: + async with session.get(f"{url}/api/ankerctl/status") as response: + # TODO: Temporary if on the main branch of ankerctl + if response.status == 404: + raise AnkerUtilException("Ankerctl API not found (not present in ankerctl yet)") + if response.status != 200: + raise AnkerUtilException(f"Failed to get api status: {response.status}") + return await response.json() + except Exception as e: + raise AnkerUtilException(f"Failed to get api status: {e}") diff --git a/custom_components/ankermake/ankermake_mqtt_adapter.py b/custom_components/ankermake/ankermake_mqtt_adapter.py index e2a4a3c..4f32d44 100644 --- a/custom_components/ankermake/ankermake_mqtt_adapter.py +++ b/custom_components/ankermake/ankermake_mqtt_adapter.py @@ -28,7 +28,9 @@ @dataclass class AnkerData: - _timezone: datetime.tzinfo = None + _timezone: datetime.tzinfo = None # Defined in __init__.py + _api_status: dict = None # Updated via __init__.py + _last_heartbeat: datetime = None _status: AnkerStatus = AnkerStatus.OFFLINE _old_status: AnkerStatus = None @@ -208,6 +210,24 @@ def _remove_error(self): self.error_message = "" self.error_level = "" + @property + def api_status(self) -> str: + return self._api_status.get('status', 'Offline').capitalize() + + @staticmethod + def api_status_possible_states() -> list: + return ['Ok', 'Error', 'Offline'] + + @property + def api_service_possible_states(self) -> list: + return list(self._api_status.get('possible_states', {}).keys()) + ['Unavailable'] + + def get_api_service_status(self, service: str) -> str: + return self._api_status.get('services', {}).get(service, {}).get('state', 'Unavailable') + + def get_api_service_online(self, service: str) -> bool: + return self._api_status.get('services', {}).get(service, {}).get('online', False) + def update(self, websocket_message: dict): """Update the AnkerData object with a new message from the AnkerMake printer.""" command_type = websocket_message.get("commandType") diff --git a/custom_components/ankermake/binary_sensor.py b/custom_components/ankermake/binary_sensor.py index 872570a..b7e36d1 100644 --- a/custom_components/ankermake/binary_sensor.py +++ b/custom_components/ankermake/binary_sensor.py @@ -36,16 +36,22 @@ def __init__(self, coordinator, description, dev_info, attrs): self.attrs = attrs.copy() self._attr_extra_state_attributes = dict() + def _filter_handler(self, key: str): + if key.startswith('%SVC_ONLINE='): + return self.coordinator.ankerdata.get_api_service_online(key.split('=')[1]) + elif key.startswith('%SVC_STATE='): + return self.coordinator.ankerdata.get_api_service_status(key.split('=')[1]) + + return getattr(self.coordinator.ankerdata, key) + @callback def _update_from_anker(self) -> None: try: - state = getattr(self.coordinator.ankerdata, self.attrs['state']) - self._attr_is_on = state - for attr, key in self.attrs.items(): if attr == 'state': + self._attr_is_on = self._filter_handler(key) continue - self._attr_extra_state_attributes[attr] = getattr(self.coordinator.ankerdata, key) + self._attr_extra_state_attributes[attr] = self._filter_handler(key) if not self.coordinator.ankerdata.online: self._attr_available = True diff --git a/custom_components/ankermake/sensor.py b/custom_components/ankermake/sensor.py index 5aa983b..8dd9f0d 100644 --- a/custom_components/ankermake/sensor.py +++ b/custom_components/ankermake/sensor.py @@ -45,18 +45,21 @@ def td_convert(seconds): def _filter_handler(self, key: str): if key.startswith('%%TD='): - val = getattr(self.coordinator.ankerdata, key[5:]) + val = getattr(self.coordinator.ankerdata, key.split('=')[1]) return self.td_convert(val) elif key.startswith('='): return key[1:] + elif key.startswith('%SVC_ONLINE='): + return self.coordinator.ankerdata.get_api_service_online(key.split('=')[1]) + elif key.startswith('%SVC_STATE='): + return self.coordinator.ankerdata.get_api_service_status(key.split('=')[1]) return getattr(self.coordinator.ankerdata, key) @callback def _update_from_anker(self) -> None: try: - state = getattr(self.coordinator.ankerdata, self.attrs['state']) - self._attr_native_value = state + self._attr_native_value = self._filter_handler(self.attrs['state']) for attr, key in self.attrs.items(): if attr == 'state': diff --git a/custom_components/ankermake/sensor_manifest.py b/custom_components/ankermake/sensor_manifest.py index 08ebf8e..e070deb 100644 --- a/custom_components/ankermake/sensor_manifest.py +++ b/custom_components/ankermake/sensor_manifest.py @@ -2,7 +2,7 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.sensor import SensorEntityDescription, SensorDeviceClass -from .ankermake_mqtt_adapter import AnkerStatus, FilamentType +from .ankermake_mqtt_adapter import AnkerStatus, FilamentType, AnkerData # Linter is complaining without this class, it is strictly unnecessary @@ -39,6 +39,62 @@ def __init__(self, *args, **kwargs): 'data_collection': 'ai_data_collection', } ], + # Filetransfer service + [Description( + key="filetransfer_service", + name="Filetransfer Service", + icon="mdi:console", + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_registry_enabled_default=False, # TODO: Enable this when API is in master + ), + { + 'state': '%SVC_ONLINE=filetransfer', + 'status': '%SVC_STATE=filetransfer', + 'possible_states': 'api_service_possible_states', + } + ], + # PPPPservice + [Description( + key="pppp_service", + name="PPPP Service", + icon="mdi:console", + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_registry_enabled_default=False, # TODO: Enable this when API is in master + ), + { + 'state': '%SVC_ONLINE=pppp', + 'status': '%SVC_STATE=pppp', + 'possible_states': 'api_service_possible_states', + } + ], + # Videoqueue + [Description( + key="videoqueue_service", + name="Videoqueue Service", + icon="mdi:console", + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_registry_enabled_default=False, # TODO: Enable this when API is in master + ), + { + 'state': '%SVC_ONLINE=videoqueue', + 'status': '%SVC_STATE=videoqueue', + 'possible_states': 'api_service_possible_states', + } + ], + # mqtt + [Description( + key="mqtt_service", + name="MQTT Service", + icon="mdi:console", + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_registry_enabled_default=False, # TODO: Enable this when API is in master + ), + { + 'state': '%SVC_ONLINE=mqtt', + 'status': '%SVC_STATE=mqtt', + 'possible_states': 'api_service_possible_states', + } + ], ] # Key must match the attribute in the AnkerData class @@ -247,4 +303,26 @@ def __init__(self, *args, **kwargs): 'error_level': 'error_level', } ], + # Ankerctl + [Description( + key="ankerctl", + name="Ankerctl", + icon="mdi:console", + entity_registry_enabled_default=False, # TODO: Enable this when API is in master + device_class='enum', + options=AnkerData.api_status_possible_states(), + ), + { + 'state': 'api_status', + 'pppp_service': '%SVC_STATE=pppp', + 'pppp_online': '%SVC_ONLINE=pppp', + 'filetransfer_service': '%SVC_STATE=filetransfer', + 'filetransfer_online': '%SVC_ONLINE=filetransfer', + 'videoqueue_service': '%SVC_STATE=videoqueue', + 'videoqueue_online': '%SVC_ONLINE=videoqueue', + 'mqtt_service': '%SVC_STATE=mqtt', + 'mqtt_online': '%SVC_ONLINE=mqtt', + 'possible_states': 'api_service_possible_states', + } + ], ] From 661b28d7e14c12153ca3f60ac82a145945610ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Tue, 9 Jul 2024 22:27:16 +0200 Subject: [PATCH 03/20] Add camera instructions (WIP) --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index 7ce3071..a62dc28 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,55 @@ Home Assistant UI by searching for "AnkerMake" or click the button below. > Note: You can add as many instances as you'd like (but you will need an ankerctl instance configured for each > printer). +## Adding a camera (WIP) + +
+ +Click to expand! + +> NOTE: This might not work for you YET! Also it isn't the most reliable feed. See PR/Draft in +> ankerctl: [here](https://github.com/Ankermgmt/ankermake-m5-protocol/pull/162) + +## Using go2rtc + +`go2rtc.yaml` (https://github.com/AlexxIT/go2rtc?tab=readme-ov-file#go2rtc-home-assistant-add-on) + +```yaml +streams: + Anker: + - ffmpeg:http://ankerctl-ip:4470/video +``` + +
+Alt: Frigate config + +Note: Frigate just runs go2rtc + +`config.yml` + +```yaml +go2rtc: + streams: + Anker: + - "ffmpeg:http://ankerctl-ip:4470/video" +``` + +
+ +## Lovelace Card (Home Assistant) + +Add WebRTC integration from HACS (https://github.com/AlexxIT/WebRTC?tab=readme-ov-file#installation) + +Use either `http://:1984` or `http://:1984` when configuring the integration, reboot and add +a `Custom: WebRTC Camera` card to the dashboard: + +```yaml +type: custom:webrtc-camera +url: Anker +``` + +
+ ## Dependencies For this component to work, you will need an instance of [ankerctl](https://github.com/Ankermgmt/ankermake-m5-protocol) @@ -55,6 +104,7 @@ There are probably many issues to list... - There are (almost) no unit tests :( - Logging is pretty much non-existent, documentation is a bit lacking - ankerctl can crash sometimes, hindering the integration from working until it's restarted +- The API isn't added to ankerctl yet (showing the service statuses, etc) ## Testing From bcbab2e1096da16e2377e2d1a88d99ea840de5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:07:11 +0200 Subject: [PATCH 04/20] Add Placeholder image --- .github/workflows/release.yml | 2 ++ custom_components/ankermake/anker_models.py | 1 + .../ankermake/ankermake_mqtt_adapter.py | 5 +++++ .../ankermake/assets/placeholder_gcode.png | Bin 0 -> 5955 bytes custom_components/ankermake/image.py | 4 ++++ 5 files changed, 12 insertions(+) create mode 100644 custom_components/ankermake/assets/placeholder_gcode.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8f2d80..c711ba1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,8 @@ on: release: types: - published + branches: + - main permissions: contents: write diff --git a/custom_components/ankermake/anker_models.py b/custom_components/ankermake/anker_models.py index eec00a6..a641379 100644 --- a/custom_components/ankermake/anker_models.py +++ b/custom_components/ankermake/anker_models.py @@ -63,6 +63,7 @@ class CommandTypes(Enum): ZZ_MQTT_CMD_AI_INFO_CHECK = 1051 # Not used ZZ_MQTT_CMD_MODEL_LAYER = 1052 TEMP_MAX_PRINT_SPEED = 1055 # max_print_speed: 500 + TEMP_PRINT_STOPPED = 1068 # {'name': 'name', 'img': 'url', 'totalTime': 0, 'filamentUsed': 0, 'filamentUnit': 'mm', 'saveTime': 0, 'trigger': 2}) UNKNOWN_1081 = 1081 # Not used UNKNOWN_1084 = 1084 # Not used TEMP_IS_LEVELED = 1072 # isLeveled: 1 diff --git a/custom_components/ankermake/ankermake_mqtt_adapter.py b/custom_components/ankermake/ankermake_mqtt_adapter.py index 4f32d44..f07ba36 100644 --- a/custom_components/ankermake/ankermake_mqtt_adapter.py +++ b/custom_components/ankermake/ankermake_mqtt_adapter.py @@ -315,6 +315,11 @@ def update(self, websocket_message: dict): case CommandTypes.TEMP_IS_LEVELED.value: self.bed_leveled = websocket_message.get("isLeveled") == 1 + # When the STOP button is pressed, this message is sent + case CommandTypes.TEMP_PRINT_STOPPED.value: + # Resetting for now, which will set state to IDLE + self._reset() + # Errors (?) case CommandTypes.TEMP_ERROR_CODE.value: self.error_level = websocket_message.get("errorLevel") diff --git a/custom_components/ankermake/assets/placeholder_gcode.png b/custom_components/ankermake/assets/placeholder_gcode.png new file mode 100644 index 0000000000000000000000000000000000000000..be3a5c71916c539891f40aa436e7fadf37dc82f8 GIT binary patch literal 5955 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 None: @@ -34,6 +36,8 @@ def _update_from_anker(self) -> None: async def async_image(self) -> bytes | None: """Return image bytes.""" + if not self._gcode_preview_url: + return await self.hass.async_add_executor_job(lambda: open(self._placeholder_path, 'rb').read()) async with aiohttp.ClientSession() as session: async with session.get(self._gcode_preview_url) as response: return await response.read() From d95adeca48a220e4c17e64e36dbe3fbcc7d9b7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:07:38 +0200 Subject: [PATCH 05/20] Create release-dev.yml --- .github/workflows/release-dev.yml | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/release-dev.yml diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml new file mode 100644 index 0000000..137db84 --- /dev/null +++ b/.github/workflows/release-dev.yml @@ -0,0 +1,43 @@ +name: Release-Dev + +on: + push: + branches: + - dev + +permissions: + contents: write + +jobs: + hacs: + name: HACS Action + runs-on: "ubuntu-latest" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main + + - name: Get tag from commit summary with abbreviated tag + run: | + echo "tag=$(git describe --tags --abbrev=0)-0" >> $GITHUB_ENV + + - name: Set version to release tag + run: | + sed -i "s/GITHUB_RELEASE_VERSION/${{ env.tag }}/g" custom_components/ankermake/manifest.json + sed -i "s/GITHUB_RELEASE_VERSION/${{ env.tag }}/g" custom_components/ankermake/const.py + + - name: Zip ankermake + run: | + cd custom_components/ankermake + zip -r ankermake.zip . + + - name: Upload release asset to the existing release + uses: svenstaro/upload-release-action@v1-release + with: + repo_token: ${{ secrets.GH_TOKEN }} + asset_path: custom_components/ankermake/ankermake.zip + asset_name: ankermake.zip + tag: ${{ env.tag }} + overwrite: true \ No newline at end of file From b189a01949813084019069e9bdd9753230b7ab5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:08:45 +0200 Subject: [PATCH 06/20] Update release-dev.yml --- .github/workflows/release-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 137db84..7fdf7dc 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -37,7 +37,7 @@ jobs: uses: svenstaro/upload-release-action@v1-release with: repo_token: ${{ secrets.GH_TOKEN }} - asset_path: custom_components/ankermake/ankermake.zip + file: custom_components/ankermake/ankermake.zip asset_name: ankermake.zip tag: ${{ env.tag }} overwrite: true \ No newline at end of file From 170d441d49d4e394ee40526dc8edfb159ce669a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:12:07 +0200 Subject: [PATCH 07/20] Update release-dev.yml --- .github/workflows/release-dev.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 7fdf7dc..3d819b9 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -36,8 +36,11 @@ jobs: - name: Upload release asset to the existing release uses: svenstaro/upload-release-action@v1-release with: - repo_token: ${{ secrets.GH_TOKEN }} + repo_token: ${{ secrets.GITHUB_TOKEN }} file: custom_components/ankermake/ankermake.zip asset_name: ankermake.zip tag: ${{ env.tag }} - overwrite: true \ No newline at end of file + overwrite: true + body: "Autogenerated prerelease (dev) version of the component. See the GitHub repository for details." + prerelease: true + target_commit: dev \ No newline at end of file From 4118986fca715860bfed6ad5ef7b46059fc38a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:12:47 +0200 Subject: [PATCH 08/20] Use v2 --- .github/workflows/release-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 3d819b9..96b553e 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -34,7 +34,7 @@ jobs: zip -r ankermake.zip . - name: Upload release asset to the existing release - uses: svenstaro/upload-release-action@v1-release + uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: custom_components/ankermake/ankermake.zip From e3dac5f9fc0c59ea28da9a131f6d21faafdcfabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:18:48 +0200 Subject: [PATCH 09/20] Use dev tag for prerelease --- .github/workflows/release-dev.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 96b553e..5abad07 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -21,7 +21,8 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | - echo "tag=$(git describe --tags --abbrev=0)-0" >> $GITHUB_ENV + echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV + echo "tag=$(echo ${{ env.tag }} | awk -F. '{print $1"."$2+1".0-0"}')" >> $GITHUB_ENV - name: Set version to release tag run: | @@ -39,7 +40,7 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} file: custom_components/ankermake/ankermake.zip asset_name: ankermake.zip - tag: ${{ env.tag }} + tag: dev overwrite: true body: "Autogenerated prerelease (dev) version of the component. See the GitHub repository for details." prerelease: true From 790fed268120c0dbd7f94e202a6be1ffe838a832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:31:38 +0200 Subject: [PATCH 10/20] Update release-dev.yml --- .github/workflows/release-dev.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 5abad07..3bae1a1 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -22,7 +22,7 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - echo "tag=$(echo ${{ env.tag }} | awk -F. '{print $1"."$2+1".0-0"}')" >> $GITHUB_ENV + echo "tag=$(echo ${{ env.tag }} | sed -E 's/([0-9]+\.[0-9]+)\.([0-9]+)/\1+1.\2\-0/g')" >> $GITHUB_ENV - name: Set version to release tag run: | @@ -40,8 +40,8 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} file: custom_components/ankermake/ankermake.zip asset_name: ankermake.zip - tag: dev + tag: ${{ env.tag }} overwrite: true body: "Autogenerated prerelease (dev) version of the component. See the GitHub repository for details." prerelease: true - target_commit: dev \ No newline at end of file + target_commit: dev From 1e1bd7439a94adb32ee30eae9137a17680cd2769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:35:23 +0200 Subject: [PATCH 11/20] Trying again.. --- .github/workflows/release-dev.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 3bae1a1..99bdabb 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -22,7 +22,8 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - echo "tag=$(echo ${{ env.tag }} | sed -E 's/([0-9]+\.[0-9]+)\.([0-9]+)/\1+1.\2\-0/g')" >> $GITHUB_ENV + echo "tag=$(echo $tag | awk -F. '{print $1"."$2+1"."$3"-0"}')" >> $GITHUB_ENV + echo "${{ env.tag }}" - name: Set version to release tag run: | From ac83c85a45e26a7c8be7b01dc29beba0f04fe43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:36:56 +0200 Subject: [PATCH 12/20] Update release-dev.yml --- .github/workflows/release-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 99bdabb..57cc42a 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -22,7 +22,7 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - echo "tag=$(echo $tag | awk -F. '{print $1"."$2+1"."$3"-0"}')" >> $GITHUB_ENV + echo "tag=$(echo ${{ env.tag }} | awk -F. '{print $1"."$2+1"."$3"-0"}')" >> $GITHUB_ENV echo "${{ env.tag }}" - name: Set version to release tag From 70537780e00db2455496ede9691e11ead74d9d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:38:37 +0200 Subject: [PATCH 13/20] Reverting to simpler times --- .github/workflows/release-dev.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 57cc42a..63141f2 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -21,9 +21,7 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | - echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - echo "tag=$(echo ${{ env.tag }} | awk -F. '{print $1"."$2+1"."$3"-0"}')" >> $GITHUB_ENV - echo "${{ env.tag }}" + echo "tag=$(git describe --tags --abbrev=0)-0" >> $GITHUB_ENV - name: Set version to release tag run: | From 54ac32e4173c13a9e7bb197ad1cdf97b8afe6a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:40:40 +0200 Subject: [PATCH 14/20] Actually push the dev branch to dev build --- .github/workflows/release-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 63141f2..99a5443 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - ref: main + ref: dev - name: Get tag from commit summary with abbreviated tag run: | From bea1929bd279ed5557a10592cbce85c308411120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:43:33 +0200 Subject: [PATCH 15/20] Update release-dev.yml --- .github/workflows/release-dev.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 99a5443..aec0ae2 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -21,7 +21,8 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | - echo "tag=$(git describe --tags --abbrev=0)-0" >> $GITHUB_ENV + tag=$(git describe --tags --abbrev=0) + echo "tag=$(echo $tag | grep -oP '\d+\.\d+\.\d+')-0" >> $GITHUB_ENV - name: Set version to release tag run: | From 785440962d980e5c7b9b42d2d75a5d8f162cbff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:51:04 +0200 Subject: [PATCH 16/20] Increment prerelease by 0.1.0-0 --- .github/workflows/release-dev.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index aec0ae2..33fa5a9 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -22,7 +22,9 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | tag=$(git describe --tags --abbrev=0) - echo "tag=$(echo $tag | grep -oP '\d+\.\d+\.\d+')-0" >> $GITHUB_ENV + tag=$(echo $tag | grep -oP '\d+\.\d+\.\d+') + tag=$(echo $tag | awk -F. '{print $1"."$2+1"."$3"-0"}') + echo "tag=$tag" >> $GITHUB_ENV - name: Set version to release tag run: | From 0d65cc4c924d7e7c070bbf08680e50a03c3ec355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:54:02 +0200 Subject: [PATCH 17/20] Only increment non pre-release tags --- .github/workflows/release-dev.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 33fa5a9..62dad0a 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -22,8 +22,10 @@ jobs: - name: Get tag from commit summary with abbreviated tag run: | tag=$(git describe --tags --abbrev=0) - tag=$(echo $tag | grep -oP '\d+\.\d+\.\d+') - tag=$(echo $tag | awk -F. '{print $1"."$2+1"."$3"-0"}') + if [[ $tag != *"-0" ]]; then + tag=$(echo $tag | grep -oP '\d+\.\d+\.\d+') + tag=$(echo $tag | awk -F. '{print $1"."$2+1"."$3"-0"}') + fi echo "tag=$tag" >> $GITHUB_ENV - name: Set version to release tag From c9376663628722360732ad9915ef4e6cc47b8fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:18:46 +0200 Subject: [PATCH 18/20] Update image.py --- custom_components/ankermake/image.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/custom_components/ankermake/image.py b/custom_components/ankermake/image.py index 9fd9eef..536a438 100644 --- a/custom_components/ankermake/image.py +++ b/custom_components/ankermake/image.py @@ -24,11 +24,14 @@ def __init__(self, coordinator, description, dev_info, hass: HomeAssistant): @callback def _update_from_anker(self) -> None: gcode_preview_url = self.coordinator.ankerdata.image - if gcode_preview_url != self._gcode_preview_url: - self._attr_image_last_updated = datetime.now() + is_new_image = gcode_preview_url != self._gcode_preview_url + self._gcode_preview_url = gcode_preview_url self._attr_image_url = self._gcode_preview_url + if is_new_image: + self._attr_image_last_updated = datetime.now() + if self.coordinator.ankerdata.online: self._attr_available = True else: From 359ca6206c6ad02e1813a34337309e51ea8ce06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:46:36 +0200 Subject: [PATCH 19/20] Add docker-compose, redo some API sensors --- README.md | 38 +++++++++++++++++++ .../ankermake/ankermake_mqtt_adapter.py | 8 ---- .../ankermake/sensor_manifest.py | 34 +++-------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a62dc28..f127009 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,44 @@ Assistant add-on in their organization, but I have not tested it with this compo (The branch of ankerctl I'm using: https://github.com/sondregronas/ankermake-m5-protocol/tree/patch-exiles-1.1-auto-restart-on-failure) +
+Click here for a docker-compose setup + +You can use this `docker-compose.yml` file to start an instance of my fork of ankerctl. Note that the container is set +to restart every 2 hours as a workaround for some socket issues I've encountered, but isn't strictly necessary. + +```yaml +services: + ankerctl: + container_name: ankerctl + restart: unless-stopped + build: + context: https://github.com/sondregronas/ankermake-m5-protocol.git#patch-exiles-1.1-auto-restart-on-failure + privileged: true + # host-mode networking is required for pppp communication with the + # printer, since it is an asymmetrical udp protocol. + network_mode: host + environment: + - FLASK_HOST=0.0.0.0 + - FLASK_PORT=4470 + volumes: + - ankerctl_vol:/root/.config/ankerctl + - ./ankermake-m5-protocol/web/:/app/web + + # This container will restart the ankerctl container every 2 hours + # as a temporary workaround for some socket issues. + ankerctl_restarter: + image: docker + volumes: ["/var/run/docker.sock:/var/run/docker.sock"] + # 2 hours = 7200 seconds + command: ["/bin/sh", "-c", "while true; do sleep 7200; docker restart ankerctl; done"] + restart: unless-stopped +volumes: + ankerctl_vol: +``` + +
+ ## Known issues There are probably many issues to list... diff --git a/custom_components/ankermake/ankermake_mqtt_adapter.py b/custom_components/ankermake/ankermake_mqtt_adapter.py index f07ba36..b334e7e 100644 --- a/custom_components/ankermake/ankermake_mqtt_adapter.py +++ b/custom_components/ankermake/ankermake_mqtt_adapter.py @@ -210,14 +210,6 @@ def _remove_error(self): self.error_message = "" self.error_level = "" - @property - def api_status(self) -> str: - return self._api_status.get('status', 'Offline').capitalize() - - @staticmethod - def api_status_possible_states() -> list: - return ['Ok', 'Error', 'Offline'] - @property def api_service_possible_states(self) -> list: return list(self._api_status.get('possible_states', {}).keys()) + ['Unavailable'] diff --git a/custom_components/ankermake/sensor_manifest.py b/custom_components/ankermake/sensor_manifest.py index e070deb..5640143 100644 --- a/custom_components/ankermake/sensor_manifest.py +++ b/custom_components/ankermake/sensor_manifest.py @@ -41,7 +41,7 @@ def __init__(self, *args, **kwargs): ], # Filetransfer service [Description( - key="filetransfer_service", + key="service_filetransfer", name="Filetransfer Service", icon="mdi:console", device_class=BinarySensorDeviceClass.CONNECTIVITY, @@ -55,7 +55,7 @@ def __init__(self, *args, **kwargs): ], # PPPPservice [Description( - key="pppp_service", + key="service_pppp", name="PPPP Service", icon="mdi:console", device_class=BinarySensorDeviceClass.CONNECTIVITY, @@ -69,7 +69,7 @@ def __init__(self, *args, **kwargs): ], # Videoqueue [Description( - key="videoqueue_service", + key="service_videoqueue", name="Videoqueue Service", icon="mdi:console", device_class=BinarySensorDeviceClass.CONNECTIVITY, @@ -83,15 +83,15 @@ def __init__(self, *args, **kwargs): ], # mqtt [Description( - key="mqtt_service", + key="service_mqtt", name="MQTT Service", icon="mdi:console", device_class=BinarySensorDeviceClass.CONNECTIVITY, entity_registry_enabled_default=False, # TODO: Enable this when API is in master ), { - 'state': '%SVC_ONLINE=mqtt', - 'status': '%SVC_STATE=mqtt', + 'state': '%SVC_ONLINE=mqttqueue', + 'status': '%SVC_STATE=mqttqueue', 'possible_states': 'api_service_possible_states', } ], @@ -303,26 +303,4 @@ def __init__(self, *args, **kwargs): 'error_level': 'error_level', } ], - # Ankerctl - [Description( - key="ankerctl", - name="Ankerctl", - icon="mdi:console", - entity_registry_enabled_default=False, # TODO: Enable this when API is in master - device_class='enum', - options=AnkerData.api_status_possible_states(), - ), - { - 'state': 'api_status', - 'pppp_service': '%SVC_STATE=pppp', - 'pppp_online': '%SVC_ONLINE=pppp', - 'filetransfer_service': '%SVC_STATE=filetransfer', - 'filetransfer_online': '%SVC_ONLINE=filetransfer', - 'videoqueue_service': '%SVC_STATE=videoqueue', - 'videoqueue_online': '%SVC_ONLINE=videoqueue', - 'mqtt_service': '%SVC_STATE=mqtt', - 'mqtt_online': '%SVC_ONLINE=mqtt', - 'possible_states': 'api_service_possible_states', - } - ], ] From aa8fed8c9da31488d51d85548febd4bb25f0af4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Gr=C3=B8n=C3=A5s?= <44143748+sondregronas@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:34:16 +0200 Subject: [PATCH 20/20] Cleanup sensors, use unique_ids --- custom_components/ankermake/__init__.py | 18 +++++++++++++++++- custom_components/ankermake/binary_sensor.py | 8 -------- custom_components/ankermake/sensor.py | 19 +------------------ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/custom_components/ankermake/__init__.py b/custom_components/ankermake/__init__.py index 74f736e..e6cfa7e 100644 --- a/custom_components/ankermake/__init__.py +++ b/custom_components/ankermake/__init__.py @@ -119,7 +119,7 @@ def __init__(self, coordinator: AnkerMakeUpdateCoordinator, self._attr_name = f"{device_info['name']} {description.name}" self.entity_description = description - self._attr_unique_id = description.key + self._attr_unique_id = f"{device_info['name']}_{description.key}" self._attr_device_info = device_info @property @@ -133,3 +133,19 @@ def _handle_coordinator_update(self) -> None: def _update_from_anker(self) -> None: """Update the entity. (Used by sensor.py)""" + + def _filter_handler(self, key: str): + def td_convert(seconds): + return str(timedelta(seconds=seconds)) + + if key.startswith('%%TD='): + val = getattr(self.coordinator.ankerdata, key.split('=')[1]) + return td_convert(val) + elif key.startswith('='): + return key[1:] + elif key.startswith('%SVC_ONLINE='): + return self.coordinator.ankerdata.get_api_service_online(key.split('=')[1]) + elif key.startswith('%SVC_STATE='): + return self.coordinator.ankerdata.get_api_service_status(key.split('=')[1]) + + return getattr(self.coordinator.ankerdata, key) diff --git a/custom_components/ankermake/binary_sensor.py b/custom_components/ankermake/binary_sensor.py index b7e36d1..29471e7 100644 --- a/custom_components/ankermake/binary_sensor.py +++ b/custom_components/ankermake/binary_sensor.py @@ -36,14 +36,6 @@ def __init__(self, coordinator, description, dev_info, attrs): self.attrs = attrs.copy() self._attr_extra_state_attributes = dict() - def _filter_handler(self, key: str): - if key.startswith('%SVC_ONLINE='): - return self.coordinator.ankerdata.get_api_service_online(key.split('=')[1]) - elif key.startswith('%SVC_STATE='): - return self.coordinator.ankerdata.get_api_service_status(key.split('=')[1]) - - return getattr(self.coordinator.ankerdata, key) - @callback def _update_from_anker(self) -> None: try: diff --git a/custom_components/ankermake/sensor.py b/custom_components/ankermake/sensor.py index 8dd9f0d..48ea549 100644 --- a/custom_components/ankermake/sensor.py +++ b/custom_components/ankermake/sensor.py @@ -21,7 +21,7 @@ class AnkerMakeSensor(AnkerMakeBaseEntity, SensorEntity): @callback def _update_from_anker(self) -> None: try: - value = getattr(self.coordinator.ankerdata, self.entity_description.key) + value = self._filter_handler(self.entity_description.key) if self.coordinator.ankerdata.online: self._attr_available = True else: @@ -39,23 +39,6 @@ def __init__(self, coordinator, description, dev_info, attrs): self.attrs = attrs.copy() self._attr_extra_state_attributes = dict() - @staticmethod - def td_convert(seconds): - return str(timedelta(seconds=seconds)) - - def _filter_handler(self, key: str): - if key.startswith('%%TD='): - val = getattr(self.coordinator.ankerdata, key.split('=')[1]) - return self.td_convert(val) - elif key.startswith('='): - return key[1:] - elif key.startswith('%SVC_ONLINE='): - return self.coordinator.ankerdata.get_api_service_online(key.split('=')[1]) - elif key.startswith('%SVC_STATE='): - return self.coordinator.ankerdata.get_api_service_status(key.split('=')[1]) - - return getattr(self.coordinator.ankerdata, key) - @callback def _update_from_anker(self) -> None: try: