make-all / tuya-local

Local support for Tuya devices in Home Assistant
MIT License
1.31k stars 513 forks source link

Request support for Petlibro feeder with camera #759

Closed kshepard339 closed 1 year ago

kshepard339 commented 1 year ago

Log Message

Device matches None with quality of 0%. DPS: {"updated_at": 1685733132.0985677, "101": true, "103": false, "105": false, "106": "0", "108": "0", "109": "31243360|3030192|28213168", "110": 1, "111": true, "117": 100, "134": false, "139": false, "140": "0", "150": true, "151": "2", "231": "7f060002017f101e04010011000200", "233": "standby", "235": 100, "236": 0, "237": "{\"value\":2,\"time\":1685700019}", "238": 2, "239": "record_stop", "240": "no_voice", "241": "full", "242": "record_end", "244": true, "247": "{\"value\":1,\"time\":1685666184}"}
Report this to https://github.com/make-all/tuya-local/issues/

Information about DPS mappings

Device Specification Attribute from iot.tuya:
{
  "result": {
    "category": "sp",
    "functions": [
      {
        "code": "basic_indicator",
        "dp_id": 101,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "basic_flip",
        "dp_id": 103,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "basic_private",
        "dp_id": 105,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "motion_sensitivity",
        "dp_id": 106,
        "type": "Enum",
        "values": "{\"range\":[\"0\",\"1\",\"2\"]}"
      },
      {
        "code": "basic_nightvision",
        "dp_id": 108,
        "type": "Enum",
        "values": "{\"range\":[\"0\",\"1\",\"2\"]}"
      },
      {
        "code": "sd_format",
        "dp_id": 111,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "motion_record",
        "dp_id": 113,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "motion_switch",
        "dp_id": 134,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "decibel_switch",
        "dp_id": 139,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "decibel_sensitivity",
        "dp_id": 140,
        "type": "Enum",
        "values": "{\"range\":[\"0\",\"1\"]}"
      },
      {
        "code": "record_switch",
        "dp_id": 150,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "record_mode",
        "dp_id": 151,
        "type": "Enum",
        "values": "{\"range\":[\"1\",\"2\"]}"
      }
    ],
    "lang_config": {},
    "status": [
      {
        "code": "basic_indicator",
        "dp_id": 101,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "basic_flip",
        "dp_id": 103,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "basic_private",
        "dp_id": 105,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "motion_sensitivity",
        "dp_id": 106,
        "type": "Enum",
        "values": "{\"range\":[\"0\",\"1\",\"2\"]}"
      },
      {
        "code": "basic_nightvision",
        "dp_id": 108,
        "type": "Enum",
        "values": "{\"range\":[\"0\",\"1\",\"2\"]}"
      },
      {
        "code": "sd_storge",
        "dp_id": 109,
        "type": "String",
        "values": "{\"maxlen\":255}"
      },
      {
        "code": "sd_status",
        "dp_id": 110,
        "type": "Integer",
        "values": "{\"min\":1,\"max\":5,\"scale\":0,\"step\":1}"
      },
      {
        "code": "sd_format",
        "dp_id": 111,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "motion_record",
        "dp_id": 113,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "movement_detect_pic",
        "dp_id": 115,
        "type": "Raw",
        "values": "{}"
      },
      {
        "code": "sd_format_state",
        "dp_id": 117,
        "type": "Integer",
        "values": "{\"min\":-20000,\"max\":200000,\"scale\":0,\"step\":1}"
      },
      {
        "code": "motion_switch",
        "dp_id": 134,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "decibel_switch",
        "dp_id": 139,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "decibel_sensitivity",
        "dp_id": 140,
        "type": "Enum",
        "values": "{\"range\":[\"0\",\"1\"]}"
      },
      {
        "code": "decibel_upload",
        "dp_id": 141,
        "type": "String",
        "values": "{\"maxlen\":255}"
      },
      {
        "code": "record_switch",
        "dp_id": 150,
        "type": "Boolean",
        "values": "{}"
      },
      {
        "code": "record_mode",
        "dp_id": 151,
        "type": "Enum",
        "values": "{\"range\":[\"1\",\"2\"]}"
      },
      {
        "code": "initiative_message",
        "dp_id": 212,
        "type": "Raw",
        "values": "{}"
      }
    ]
  },
  "success": true,
  "t": 1685733254232,
  "tid": "a988f986017911ee8adfb6b4e10a8863"
}

These are from device logs on iot.tuya. I added -> and then notes to include additional info I found through testing:
134 Motion Detection
139 Sound Detection
140 Sound Detection Sensitivity -> Here are the values and what they correlate to:
    0 = Low
    1 = High
141 Sound Detected
212 Active message push
150 Video Recording
151 Video Recording Mode -> Here are the values and what they correlate to:
    1 = Event Recording
    2 = Non-Stop
101 Indicator -> Turns on/off the display of the controls on feeder
103 Image Flip -> Rotates the camera feed stream 180 degrees
231 Schedule -> Not sure what these values correlate to but log shows a couple of entries:
    7f060002017f101e04010011000200
    7f060002017f101e04010011000201
232 Manual Feeding -> Set value trigger feeder to deliver that # of portions
105 Private Mode
233 Feeding State -> Displays status of feeder. Values appear to be:
    feeding
    standby
106 Motion Detection Sensitivity -> Here are the values and what they correlate to:
    0 = Low
    1 = Medium
    2 = High
235 Battery -> Displays battery level
108 Infrared Night Vision -> Here are the values and what they correlate to:
    0 = Auto
    1 = Off
    2 = On
236 Fault -> Displays error codes. Error codes I have produced:
    1 -> In the app it showed: no more food
    2 -> In the app it showed: outlet is blocked
    3 -> In the app it showed: no more food,outlet is blocked
    8 -> In the app it showed: Low Battery
109 Get Memory Card Capacity -> This is a 32GB sd card, log appears to have Total Capacity|Used|Remaining. Log value is:
    31243360|3218896|28024464
237 Plan Feed Report -> Displays last time a schedule feed occurred; portions dispensed and time. Log shows:
    {"value":4,"time":1685737838}   
110 Memory Card State
238 Record Play Times -> Used to set number of times to play voice recording. App lets you select 0-10
111 Memory Card Format
239 Recording Settings -> Displays what recorder action. Values I found
    record_start -> When press button to start recording voice
    record_stop -> When press button to stop recording voice
    player -> When you play voice recording by pressing play button in app
    del -> When you delete voice recording 
240 Record file -> Displays if a voice recording has been saved. Values I found are:
    have_voice
    no_voice
113 Event Recording
241 Food Level -> Displays status of food level in container. Values I found are:
    full
    lack
242 Recording State -> Appears to display when a recording was started and ended. Values I found:
    record_start
    record_end
115 Motion Detection
243 Recording Length -> Nothing was listed in the logs
244 Log -> Switch log on/off
117 Format State
245 Indicator -> Not sure what this indicator is, nothing in logs
246 WIFI Signal -> Nothing in the logs
247 Manual Feed Report -> Displays last time a manual feeding occurred; portions dispensed and time. Log shows:
    {"value":1,"time":1685666184}
248 Record File Name -> Shows name of recorded voice file (or blank if no recording), such as: 20230602164853.MP3
249 Data Service 1 (custom) -> Nothing in the logs
250 Data Service 2 (custom) -> Nothing in the logs

Product ID

product_id: "e1zs835joz1lc85q" product_name: "PLAF203 Camera Feeder" "model": "PLAF203"

Information about how the device functions

Product URL: https://petlibro.com/products/petlibro-granary-automatic-pet-feeder-with-camera?variant=40913462001711

Manual URL: https://cdn.shopify.com/s/files/1/0252/5197/1119/files/PETLIBRO_Granary_Camera_Monitoring_Feeder_PLAF203_USER_MANUAL.pdf?v=1682306122

kshepard339 commented 1 year ago

Thank you for building this out. I just updated to the latest version and tried to setup the feeder. I was getting connection error, so I reset the feeder and tried again. I am know getting the Sorry, there is no support for this device message. Here are the logs: Device matches None with quality of 0%. DPS: {"updated_at": 1686703712.5767665, "101": true, "103": false, "105": false, "106": "0", "108": "0", "109": "31243360|29811024|1432336", "110": 1, "117": 0, "134": false, "139": false, "150": false, "151": "2", "231": "", "235": 100, "238": 2, "239": "record_stop", "240": "no_voice", "241": "full", "242": "record_end", "244": true} Report this to https://github.com/make-all/tuya-local/issues/

I entered the id and key, and also selected 3.4 (but then tried all the other protocols). Not sure if it helps but just to test, I installed Local Tuya and tried the id and key and it connected with protocol 3.4 (it showed I could set these DPS: 101,103,105,106,108,109,110,117,134,139,151,231,235,238,239,240,241,242,244).

Please let me know if there is any other testing you want me to do. Thank you.

kshepard339 commented 1 year ago

I updated my install with the file containing the optional updates. It did install! The switches were greyed out. I restarted home assistant and some switches looked to be on, if I turn them off in Home Assistant they turn right back on (and I don't see it reflect in the Tuya app). If I make a change in the app it is not reflected in HA.

When I was testing in local tuya just to confirm my connections (i completely removed local tuya before testing again in Tuya Local), I noticed that I was able to control/see the dps I listed in my last statement (regarding turning indicator on/off, ir on/off, see the sensors, etc); it looks like you can control the some functions of the device but the actual camera feed is only in Tuya app. Also, the other tuya did not give me the option for manual feed; I had to create a script to set the dps: alias: Manual Feed1 sequence:

I hope this info helps to narrow down the cause. Let me know if screen shots will help or if there is something else I can test. Thanks! First-installed After-reboot

kshepard339 commented 1 year ago

I started some more testing but need to do more later. Line 79 changing entity camera to switch lets me turn camera on/off and then allows flip switch to work.

make-all commented 1 year ago

The camera entity also has on/off services, you shouldn't need to cripple the implementation by changing it to a switch.

The aim should be to get the device supported the best it can be in Home Assistant, not to replicate what you hacked together within the limitations of localtuya.

Regarding the issues you are having, it isn't clear. Are there any log messages?

kshepard339 commented 1 year ago

Hi, makes total sense to get the device supported the best it can be in Home Assistant and I like how you do this for all the devices. I put the code back so it is the camera entity again. I deleted the feeder and re-added it. I see these errors in the log:

Logger: homeassistant.components.camera Source: components/camera/init.py:461 Integration: Camera (documentation, issues) First occurred: 6:07:27 AM (2 occurrences) Last logged: 6:07:27 AM

Error adding entities for domain camera with platform tuya_local Error while setting up tuya_local platform for camera Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 471, in async_add_entities await asyncio.gather(*tasks) File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 749, in _async_add_entity await entity.add_to_platform_finish() File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 850, in add_to_platform_finish self.async_write_ha_state() File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 590, in async_write_ha_state self._async_write_ha_state() File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 673, in _async_write_ha_state if (entity_picture := self.entity_picture) is not None: ^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/camera/init.py", line 461, in entity_picture return ENTITY_IMAGE_URL.format(self.entity_id, self.access_tokens[-1]) ^^^^^^^^^^^^^^^^^^ AttributeError: 'TuyaLocalCamera' object has no attribute 'access_tokens'

The other log message shows:

This error originated from a custom integration.

Logger: custom_components.tuya_local.device Source: components/camera/init.py:652 Integration: Tuya Local (documentation, issues) First occurred: 6:07:27 AM (1 occurrences) Last logged: 6:07:27 AM

Petlibro Camera Feeder receive loop terminated by exception 'TuyaLocalCamera' object has no attribute 'access_tokens' Traceback (most recent call last): File "/config/custom_components/tuya_local/device.py", line 218, in receive_loop entity.async_write_ha_state() File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 590, in async_write_ha_state self._async_write_ha_state() File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 656, in _async_write_ha_state attr.update(self.state_attributes or {}) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/camera/init.py", line 652, in state_attributes attrs = {"access_token": self.access_tokens[-1]} ^^^^^^^^^^^^^^^^^^ AttributeError: 'TuyaLocalCamera' object has no attribute 'access_tokens'

make-all commented 1 year ago

Strange, there is no mention of a requirement to support access_tokens here https://developers.home-assistant.io/docs/core/entity/camera

kshepard339 commented 1 year ago

Very strange that the documentation doesn't mention access_tokens but it is causing an error.

Not related to the camera, but another issue I found is when selecting a number on manual feed, if I change it to 1 to dispense 1 serving this pops up at bottom of the screen: Failed to call service number/set_value.type NoneType doesn't defineroundmethod

This is in the log file: Logger: homeassistant.components.websocket_api.http.connection Source: custom_components/tuya_local/helpers/device_config.py:610 Integration: Home Assistant WebSocket API (documentation, issues) First occurred: 6:15:39 AM (2 occurrences) Last logged: 6:17:35 AM

[139684807106768] type NoneType doesn't define round method Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 205, in handle_call_service await hass.services.async_call( File "/usr/src/homeassistant/homeassistant/core.py", line 1910, in async_call task.result() File "/usr/src/homeassistant/homeassistant/core.py", line 1950, in _execute_service await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)( File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 226, in handle_service await service.entity_service_call( File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 811, in entity_service_call future.result() # pop exception if have ^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 1034, in async_request_call await coro File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 851, in _handle_entity_call await result File "/usr/src/homeassistant/homeassistant/components/number/init.py", line 106, in async_set_value await entity.async_set_native_value(native_value) File "/config/custom_components/tuya_local/number.py", line 104, in async_set_native_value await self._value_dps.async_set_value(self._device, value) File "/config/custom_components/tuya_local/helpers/device_config.py", line 445, in async_set_value settings = self.get_values_to_set(device, value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/config/custom_components/tuya_local/helpers/device_config.py", line 862, in get_values_to_set dps_map[self.id] = self._correct_type(result) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/config/custom_components/tuya_local/helpers/device_config.py", line 610, in _correct_type result = int(round(result)) ^^^^^^^^^^^^^ TypeError: type NoneType doesn't define round method

make-all commented 1 year ago

I think I have fixed both the above problems. It turns out Camera has an init method that sets up some internal variables used internally. No other entity has this, so it was unexpected.

The second problem is because there is a mapping of null to 0 so that the number entity does not go unavailable when no value is being reported by the device. But due to nearest match code for numeric mappings, if that is the only mapping it matches to everything, which wasn't the intention. The null mapping shouldn't be applied in reverse even if the value is an exact match, as we never (I think) want to send null as a dp value.

kshepard339 commented 1 year ago

Great job! I was able to confirm:

The only 2 things I didn't see on the interface are:

Sensors and diagnostic are reporting correctly (only thing I couldn't test was battery because device is always plugged in so batteries stay full. Batteries are just to feed on schedule set in the app as emergency backup - wifi turns off when on battery ).

If it loses power and comes back online the status states unknown but the first time it dispenses food it status correctly updates to feeding then standby; not a big deal since it gets the right status during next feeding.

Configuration components all working.

Again, great job. I am impressed with the layout and how everything is working!! Working-2switches-missing

kshepard339 commented 1 year ago

I am using the camera.py file that was updated a few hours ago. Here is the error I am seeing in the logs, maybe this will help point towards why the camera on/off and record on/off switch aren't visible in the configuration section.

This error originated from a custom integration.

Logger: aiohttp.server Source: custom_components/tuya_local/camera.py:71 Integration: Tuya Local (documentation, issues) First occurred: 6:12:48 PM (1 occurrences) Last logged: 6:12:48 PM

Error handling request Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/aiohttp/web_protocol.py", line 433, in _handle_request resp = await request_handler(request) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/aiohttp/web_app.py", line 504, in _handle resp = await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/aiohttp/web_middlewares.py", line 117, in impl return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 85, in security_filter_middleware return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 227, in forwarded_middleware return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 28, in request_context_middleware return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/http/ban.py", line 80, in ban_middleware return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 236, in auth_middleware return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/http/view.py", line 148, in handle result = await handler(request, **request.match_info) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/camera/init.py", line 734, in get if not camera.is_on: ^^^^^^^^^^^^ File "/config/custom_components/tuya_local/camera.py", line 71, in is_on return self._switch_dp.get_value(self.device) ^^^^^^^^^^^ AttributeError: 'TuyaLocalCamera' object has no attribute 'device'

make-all commented 1 year ago

Thanks for the log, I think this one is now fixed too.

kshepard339 commented 1 year ago

Thanks. I do not see that error now, but Camera on/off and record on/off still not displaying in the interface.

make-all commented 1 year ago

I don't have any cameras, so I am not familiar with how they are expected to show in the UI. From screenshots, I don't see any of these controls, so I expect they are either in the more-info dialog, or only available by service calls.

If the camera responds to Call Service camera.turn_off, camera.turn_on, then the on/off is set up correctly. Looking at the docs though, the camera needs to support streaming before the record feature will work, so maybe that needs to be split out to a seperate switch.

kshepard339 commented 1 year ago

This is my first Tuya device with a camera so not too sure about what would be shown. Unfortunately, I tried scanning the ports of the feeder but I can't get a stream; I think they aren't exposing it locally.

As a test, I added Camera (105) and Record (150) as switches to my petlibro yaml file (I left everything else as you had it) and that allowed me to toggle on/off each of them and I verified it was reflected in the Tuya app.

Once I tested that, I noticed in the Sensor section on the page 'Camera' is actual the Recording Status; turning camera on/off did not change the sensor but turning the new Record switch I put in had the sensor change between Recording and Idle.

Does it make sense to change the 'Camera' label in the Sensors section to Recorder and add Camera and Record in the config section as switches. I think that gives full control of everything needed.

kshepard339 commented 1 year ago

I meant to add that I tested using Call Service camera.turn_off, camera.turn_on and it did work. I only tested adding it as a switch too because it wasn't showing in the config section as a toggle and it is good to see and access it there with all the other options.

make-all commented 1 year ago

If you need a switch in the UI, you can use an input helper, and call the turn off/on services from an automation triggered by the helper.

kshepard339 commented 1 year ago

Yes, the camera can be done via an input helper but wouldn't record need to be split out to a separate switch since I do not see a service to call for the recorder in the HA developer tools screen.

make-all commented 1 year ago

Yes, record seems to not work as I expected (it expects HA to be doing the recording rather than the device itself) so will need to be split out.

kshepard339 commented 1 year ago

Thanks for adding the record toggle. Here are my findings: In Controls section:

In Sensors section

In Configuration section:

kshepard339 commented 1 year ago

All looks good, thanks for all the work on this!!

PtolemyIV commented 9 months ago

Thanks for all the effort on this - my PetLibro camera feeder now integrates and very impressed to see the camera feed working (when PetLibro support stressed to me this was fundamentally not accessible!)

Oddly though whilst I get access to the camera and motion settings in the entity page in HA, I do not see anything related to the feeding? i.e. no manual feed in configuration or amount etc. Is there a simple fix to this by chance?

EDIT: Ignore me, was being an idiot - I was attempting this with the standard Tuya integration that doesn't appear to expose any of the feeding functionality (albeit does expose the camera video feed)

I have now partially set up Local Tuya which exposes as per the above although also need to set up a script to execute the manual feeding function. I had to tweak your script as follows to get it to work by deleting the mode function which was I found was unsupported:

device_id: [insert device id from iot]
dp: 232
value: 1

Presumably, the only way to get the camera feed is by also using the Tuya integration?

make-all commented 9 months ago

Some Tuya cameras are standard WebRTC or ONVIF cameras that can be supported by other Home Assistant integrations if you can find the video stream address and password details. There is a tutorial here: https://shop.tuyaoem.com/add-tuya-security-camera-home-assistant-webrtc

I think the standard tuya integration is probably streaming via the tuya cloud.

PtolemyIV commented 9 months ago

Thanks - its the PLAF203 model and I can't find any settings page in the Tuya Smart app that mentions ONVIF - have a feeling this one doesn't expose an RTSP feed sadly but if anyone knows differently..

PtolemyIV commented 9 months ago

A small contribution in case it is not already obvious but if you want to show the dates of the last manual / scheduled feeds, you can use a template helper as follows:

  1. Go to "Settings -> Devices and Services -> Helpers"

  2. Select "+Add Helper"

  3. Select "Template" helper

  4. Use the following template pointing to the relevant Tuya attribute

`{% set x = states('sensor.last_manual_feed').split(',')[1] |replace('"time":', '') |replace('}', '') %}

{{ x | int | timestamp_custom("%d/%m/%Y %H:%M")}}`

This will then extract into a simple format (no TZ) and you can also extract the number of portions

My interface now looks like this - happy to share templates if of interest (double-clicking manual feed will provide a single portion)

ZANELUNA

hadifarnoud commented 8 months ago

I have plaf203_56 (I think 56 is the version of the software?). would this support plaf203 too?

hadifarnoud commented 8 months ago

I did manage to get PLAF203 working with HA but still have issues with Camera. I have latest version and followed the step by step guide in readme

Screenshot 1402-12-04 at 5 46 00 pm